ReferenceCampaigns
Campaign graph schema
A saved campaign graph is two arrays: nodes, each carrying a type and a configuration object, and edges, each joining one node’s numbered output to another node’s input. Nothing else is stored on the graph — the campaign name, its page, and its active state live on the campaign, not in the graph. Saving writes a new version of both arrays.
Node fields
Section titled “Node fields”| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Identifies the node inside this graph, and is what edges point at. Replaced with a fresh identifier when the graph is saved, so it is stable only within one version. |
type | string | yes | One of the types in node types, or one of the runtime-only types listed below. |
config | object | yes | The node’s settings. An empty object is valid, and is what a freshly added node starts with. |
canvas_x | number | yes | Horizontal position of the node on the canvas. Restored when the graph is opened. |
canvas_y | number | yes | Vertical position of the node on the canvas. Restored when the graph is opened. |
Edge fields
Section titled “Edge fields”| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Identifies the connection inside this graph. |
from_node_id | string | yes | The id of the node the connection leaves. Must appear in the same nodes array, or the connection is dropped on save. |
to_node_id | string | yes | The id of the node the connection enters. Must appear in the same nodes array, or the connection is dropped on save. |
output_index | number | yes | Which output of the source node the connection leaves from. 0 is the default output; on a condition, 0 is Match and 1 is No Match; on a random_split, 0 to N-1 are the arms in configured order. |
label | string | no | Display text shown on the connection, taken from the name of the source output — for example Match or No Match. Executions are routed by output_index, never by label. |
Worked example
Section titled “Worked example”A comment triggers the campaign, a keyword list decides the branch, matching people get a direct message and a tag an hour later, and every other execution ends immediately.
{ "nodes": [ { "id": "3f6c1a80-5d2b-4c11-9e64-7a0b8d43f215", "type": "trigger_comment", "config": { "post_scope": "all", "unique_per_user": false }, "canvas_x": 240, "canvas_y": 40 }, { "id": "9b1d47e2-08af-4d5c-bb37-2c6e5f90a4d1", "type": "condition", "config": { "match_type": "keyword_list", "keyword_list_id": 41, "reverse_keywords": false, "wait_for_reply": false }, "canvas_x": 240, "canvas_y": 200 }, { "id": "c74a2f19-6e30-4b8d-8f52-19d7c3b6e084", "type": "send_dm", "config": { "message_type": "text", "text": "Thanks for commenting. The price list is on its way." }, "canvas_x": 80, "canvas_y": 380 }, { "id": "1a58e63d-4c97-42f0-9b18-6d2f7ae5c390", "type": "delay", "config": { "delay_seconds": 3600, "only_if_no_reply": true }, "canvas_x": 80, "canvas_y": 540 }, { "id": "e2079b45-13cd-4a6e-8c71-5f83d0b29a6c", "type": "add_tag", "config": { "tag_ids": [12] }, "canvas_x": 80, "canvas_y": 700 }, { "id": "7d3fb0c6-92e1-4f57-a840-3b6c1d5e2087", "type": "stop", "config": {}, "canvas_x": 440, "canvas_y": 380 } ], "edges": [ { "id": "5c8e1b73-2f40-4d19-9a6b-8e7d0c34f512", "from_node_id": "3f6c1a80-5d2b-4c11-9e64-7a0b8d43f215", "to_node_id": "9b1d47e2-08af-4d5c-bb37-2c6e5f90a4d1", "output_index": 0, "label": "" }, { "id": "b06d4a92-7e15-48c3-bf20-6a9c2d813e47", "from_node_id": "9b1d47e2-08af-4d5c-bb37-2c6e5f90a4d1", "to_node_id": "c74a2f19-6e30-4b8d-8f52-19d7c3b6e084", "output_index": 0, "label": "Match" }, { "id": "48f1c25e-3b7d-4096-8ea4-1c5b7f02d9a3", "from_node_id": "9b1d47e2-08af-4d5c-bb37-2c6e5f90a4d1", "to_node_id": "7d3fb0c6-92e1-4f57-a840-3b6c1d5e2087", "output_index": 1, "label": "No Match" }, { "id": "a91b7e04-6c58-4d2f-93a1-7e04b8c5f261", "from_node_id": "c74a2f19-6e30-4b8d-8f52-19d7c3b6e084", "to_node_id": "1a58e63d-4c97-42f0-9b18-6d2f7ae5c390", "output_index": 0, "label": "" }, { "id": "d5720af8-9134-4be6-8c07-2f6a5d18b3e9", "from_node_id": "1a58e63d-4c97-42f0-9b18-6d2f7ae5c390", "to_node_id": "e2079b45-13cd-4a6e-8c71-5f83d0b29a6c", "output_index": 0, "label": "" } ]}Configuration keys by node type
Section titled “Configuration keys by node type”| Node type | Keys in config |
|---|---|
trigger_comment | post_scope (all or specific), post_id (required when post_scope is specific), unique_per_user |
trigger_dm | unique_per_user |
condition | match_type (any, contains, exact, keyword_list), text, keyword_list_id, reverse_keywords, wait_for_reply, filter_images, filter_gifs, filter_links |
send_dm | message_type (text, image, card, text_with_buttons), text, image_url, title, subtitle, buttons, buttons_text |
reply_comment | text |
send_comment | text, post_id |
hide_comment, delete_comment | also_block_user (Facebook only) |
delay | delay_seconds (1 to 31536000), only_if_no_reply |
random_split | paths, an array of { "percent": number } with each percent 0 to 100 and the total no more than 100 |
add_tag, remove_tag | tag_ids, an array of numeric tag identifiers |
stop | none |
Node types describes what each key does and what each node does when an execution reaches it.
Connection constraints
Section titled “Connection constraints”The canvas enforces these while a connection is being dragged, so an invalid connection never reaches a saved graph.
- A trigger is never the target of a connection.
- A node never connects to itself.
- A connection that would create a cycle is refused.
- A node accepts one incoming connection.
- Each output carries one outgoing connection.
- A trigger connects only to a
condition. - A
conditionnever connects to anothercondition. - Comment actions are unavailable when the trigger is
trigger_dm. - Downstream of a
delete_comment,reply_commentandhide_commentare refused.
A graph holds one trigger in practice, though the shape of the data allows more than one. A graph whose nodes have configuration errors cannot be saved at all — the canvas reports the errors instead. Build a campaign walks through assembling a graph that satisfies all of this.
Node types outside the palette
Section titled “Node types outside the palette”The runtime recognises eight further types that have no palette tile and no settings panel. A graph built on the canvas never contains them; they appear only through a path that bypasses the editor, so treat them as runtime-only rather than as features.
| Node type | What the execution does |
|---|---|
switch | Evaluates each arm’s rules and follows the first arm that matches, or a default arm when one is configured. With no match and no default arm the execution ends. |
wait_until | Waits until a configured time and then continues, scheduled the same way a delay is. |
block_user | Blocks the person and continues. Facebook only; on Instagram it does nothing and warns. |
unhide_comment, typing_indicator, mark_seen, set_field, note | Executable, but they carry no settings panel and the sources behind this page record no configuration for them. |
What saving does to a graph
Section titled “What saving does to a graph”- Each save writes a new version of the graph. The campaign points at the newest version, and only that version’s trigger starts new executions.
- Every node identifier is regenerated on save and the connections are remapped to the new identifiers. Identifiers are therefore stable within a version, not across saves.
- A connection whose
from_node_idorto_node_idis not in the samenodesarray is dropped, and the rest of the graph still saves. - An empty graph — both arrays empty — saves successfully. With no trigger in it, nothing starts an execution.
- An execution already in flight stays on the version it started on, so editing and saving a campaign never changes executions that are mid-flight.
- Older versions are removed once no execution refers to them.
- A
postbackbutton on asend_dmnode gets its own output. On save the canvas expands each button branch into extraconditionnodes and labels the connections with the button’s identifier; opening the graph collapses them back into button outputs. - An execution follows the connection whose
output_indexmatches the output the node selected. When no connection carries that index, the execution completes at that node. - An execution that reaches a node it has already visited ends with a cycle error, which is why the editor refuses cycles in the first place.
- The configuration keys in this reference are the ones to write. Some node types also answer to older key spellings, kept so that graphs saved earlier still run.
Related
Section titled “Related”- Node types — the full per-node reference for every key in
config - Build a campaign — building a graph that satisfies the constraints above
- Test and debug campaigns — running a saved graph and reading the path it took
- Execution states — the states an execution moves through while walking a graph