Map JSON Converter
Transform human-readable automation plans into the Sequence JSON map format.
Overview
This skill converts plans generated by sequence-map-generator into the exact JSON structure that Sequence can import. It handles node creation, rule generation, and proper positioning.
JSON Schema
Top-Level Structure
json
{
"nodes": [...],
"rules": [...],
"viewport": {
"x": 321.5,
"y": -198.4,
"zoom": 0.9
}
}
Node Schema
json
{
"id": "uuid-string",
"type": "POD | PORT | DEPOSITORY_ACCOUNT | LIABILITY_ACCOUNT",
"subtype": "CHECKING | SAVINGS | CREDIT_CARD | LOAN | null",
"name": "Human readable name",
"balance": 0,
"icon": "emoji",
"position": {
"x": 500,
"y": 300
}
}
Rule Schema
json
{
"id": "uuid-string",
"sourceId": "node-uuid",
"trigger": {
"type": "INCOMING_FUNDS | SCHEDULED | BALANCE_THRESHOLD",
"sourceId": "node-uuid",
"cron": "0 0 1 * *" // for SCHEDULED only
},
"steps": [
{
"actions": [...]
}
]
}
Action Schema
json
{
"type": "PERCENTAGE | FIXED | REMAINDER | TOP_UP | AVALANCHE | SNOWBALL | NEXT_PAYMENT_MINIMUM | TOTAL_AMOUNT_DUE | ROUND_DOWN",
"sourceId": "node-uuid",
"destinationId": "node-uuid",
"amountInCents": 10000,
"amountInPercentage": 0,
"groupIndex": 0,
"limit": null,
"upToEnabled": null
}
Conversion Process
Step 1: Parse Human-Readable Plan
Extract from the plan:
- •Node names and types
- •Rule triggers and actions
- •Relationships between nodes
Step 2: Generate Nodes
python
import uuid
def create_node(name, node_type, subtype=None, balance=0):
icons = {
'POD': '💰',
'PORT': '📥',
'DEPOSITORY_ACCOUNT': '🏦',
'LIABILITY_ACCOUNT': '💳'
}
return {
'id': str(uuid.uuid4()),
'type': node_type,
'subtype': subtype,
'name': name,
'balance': balance,
'icon': icons.get(node_type, '💰'),
'position': {'x': 0, 'y': 0} # Calculate later
}
Step 3: Calculate Positions
Arrange nodes in a logical flow:
python
def calculate_positions(nodes, rules):
"""
Layout strategy:
- Income sources (PORT) on left
- Router/allocation pods in middle
- Destination pods/accounts on right
- Liabilities at bottom
"""
positions = {}
x_start, y_start = 200, 200
spacing_x, spacing_y = 250, 150
# Group by type
ports = [n for n in nodes if n['type'] == 'PORT']
pods = [n for n in nodes if n['type'] == 'POD']
accounts = [n for n in nodes if n['type'] == 'DEPOSITORY_ACCOUNT']
liabilities = [n for n in nodes if n['type'] == 'LIABILITY_ACCOUNT']
# Position ports on left
for i, node in enumerate(ports):
positions[node['id']] = {'x': x_start, 'y': y_start + i * spacing_y}
# Position pods in middle columns
for i, node in enumerate(pods):
col = i // 4
row = i % 4
positions[node['id']] = {
'x': x_start + spacing_x + col * spacing_x,
'y': y_start + row * spacing_y
}
# Position accounts on right
for i, node in enumerate(accounts):
positions[node['id']] = {
'x': x_start + spacing_x * 3,
'y': y_start + i * spacing_y
}
# Position liabilities at bottom
for i, node in enumerate(liabilities):
positions[node['id']] = {
'x': x_start + i * spacing_x,
'y': y_start + spacing_y * 4
}
return positions
Step 4: Generate Rules
python
def create_rule(source_id, trigger_type, actions, cron=None):
trigger = {
'type': trigger_type,
'sourceId': source_id,
'cron': cron
}
return {
'id': str(uuid.uuid4()),
'sourceId': source_id,
'trigger': trigger,
'steps': [{'actions': actions}]
}
def create_action(action_type, source_id, dest_id, amount=None, percentage=None, group_index=0):
action = {
'type': action_type,
'sourceId': source_id,
'destinationId': dest_id,
'amountInCents': int(amount * 100) if amount else 0,
'amountInPercentage': percentage or 0,
'groupIndex': group_index,
'limit': None,
'upToEnabled': None
}
return action
Action Type Mapping
| Human Description | Action Type | Key Fields |
|---|---|---|
| "X% of funds" | PERCENTAGE | amountInPercentage |
| "$X moves" | FIXED | amountInCents |
| "Remaining funds" | REMAINDER | - |
| "Fill up to $X" | TOP_UP | limit (cents) |
| "Pay highest interest first" | AVALANCHE | groupIndex (same for all) |
| "Pay smallest balance first" | SNOWBALL | groupIndex (same for all) |
| "Pay minimum due" | NEXT_PAYMENT_MINIMUM | upToEnabled |
| "Pay full balance" | TOTAL_AMOUNT_DUE | - |
| "Round down to nearest $X" | ROUND_DOWN | - |
Trigger Type Mapping
| Human Description | Trigger Type | Additional Fields |
|---|---|---|
| "When funds are received" | INCOMING_FUNDS | - |
| "On the Xth of month" | SCHEDULED | cron: "0 0 X * *" |
| "Every week on DAY" | SCHEDULED | cron: "0 0 * * DAY" |
| "When balance reaches $X" | BALANCE_THRESHOLD | condition in trigger |
Cron Patterns
code
┌───────────── minute (0-59) │ ┌───────────── hour (0-23) │ │ ┌───────────── day of month (1-31) │ │ │ ┌───────────── month (1-12) │ │ │ │ ┌───────────── day of week (0-6, SUN-SAT) │ │ │ │ │ * * * * *
Common patterns:
- •
0 0 1 * *- 1st of every month - •
0 0 15 * *- 15th of every month - •
0 0 * * MON- Every Monday - •
0 0 * * FRI- Every Friday - •
0 0 1,15 * *- 1st and 15th of month
Complete Conversion Example
Input Plan:
code
Goal: Pay off credit cards Nodes: - Paycheck (PORT) - Budget Router (POD) - Bills Pod (POD) - Debt Payment Pod (POD) - Chase Card (LIABILITY_ACCOUNT, CREDIT_CARD) - Amex Card (LIABILITY_ACCOUNT, CREDIT_CARD) Rules: 1. When Paycheck receives funds: 100% to Budget Router 2. When Budget Router receives funds: - 60% to Bills Pod - 40% to Debt Payment Pod 3. When Debt Payment Pod receives funds: - AVALANCHE to Chase Card, Amex Card
Output JSON:
json
{
"nodes": [
{
"id": "node-paycheck",
"type": "PORT",
"name": "Paycheck",
"icon": "📥",
"position": {"x": 200, "y": 200}
},
{
"id": "node-router",
"type": "POD",
"name": "Budget Router",
"icon": "💰",
"position": {"x": 450, "y": 200}
},
{
"id": "node-bills",
"type": "POD",
"name": "Bills Pod",
"icon": "💰",
"position": {"x": 700, "y": 150}
},
{
"id": "node-debt",
"type": "POD",
"name": "Debt Payment Pod",
"icon": "💰",
"position": {"x": 700, "y": 300}
},
{
"id": "node-chase",
"type": "LIABILITY_ACCOUNT",
"subtype": "CREDIT_CARD",
"name": "Chase Card",
"icon": "💳",
"position": {"x": 400, "y": 500}
},
{
"id": "node-amex",
"type": "LIABILITY_ACCOUNT",
"subtype": "CREDIT_CARD",
"name": "Amex Card",
"icon": "💳",
"position": {"x": 650, "y": 500}
}
],
"rules": [
{
"id": "rule-1",
"sourceId": "node-paycheck",
"trigger": {
"type": "INCOMING_FUNDS",
"sourceId": "node-paycheck"
},
"steps": [{
"actions": [{
"type": "PERCENTAGE",
"sourceId": "node-paycheck",
"destinationId": "node-router",
"amountInPercentage": 100,
"amountInCents": 0,
"groupIndex": 0
}]
}]
},
{
"id": "rule-2",
"sourceId": "node-router",
"trigger": {
"type": "INCOMING_FUNDS",
"sourceId": "node-router"
},
"steps": [{
"actions": [
{
"type": "PERCENTAGE",
"sourceId": "node-router",
"destinationId": "node-bills",
"amountInPercentage": 60,
"groupIndex": 0
},
{
"type": "PERCENTAGE",
"sourceId": "node-router",
"destinationId": "node-debt",
"amountInPercentage": 40,
"groupIndex": 1
}
]
}]
},
{
"id": "rule-3",
"sourceId": "node-debt",
"trigger": {
"type": "INCOMING_FUNDS",
"sourceId": "node-debt"
},
"steps": [{
"actions": [
{
"type": "AVALANCHE",
"sourceId": "node-debt",
"destinationId": "node-chase",
"amountInPercentage": 100,
"groupIndex": 0
},
{
"type": "AVALANCHE",
"sourceId": "node-debt",
"destinationId": "node-amex",
"amountInPercentage": 100,
"groupIndex": 0
}
]
}]
}
],
"viewport": {
"x": 300,
"y": 100,
"zoom": 0.9
}
}
References
- •schema.md - Complete JSON schema specification