AI-Friendly API
How the automation API is shaped so AI agents and MCP servers can construct working flows in a single call.
The automation API is designed so an AI agent — calling the REST endpoints directly or through an MCP server — can construct a working flow without guessing enum values, without round-tripping generated IDs, and without stitching together multiple calls.
Design principles
- One call creates the full automation.
POST /v1/automationswith atemplatefield generates the whole graph + entrypoints server-side. For custom flows,POSTto create an empty automation and follow up withPUT /v1/automations/{id}/graphcontaining the full node/edge structure. - Human-chosen string keys instead of generated IDs. You choose
"welcome","ask_email","send_guide". Edges reference those keys directly. No ID dance, no second round-trip to learn what the server assigned. - Compact node vocabulary. 10 node kinds (not 90). A single
messagenode hosts all content variants via typedblocks. A singleaction_groupnode hosts all side-effect actions. The JSON schema is small enough for an LLM to keep fully in context. - Free-text kinds, not enums. Node
kind, entrypointkind, portkey, actiontype, and exit reasons are all plain strings with app-level validation. Adding a kind is a code change, not a DB migration — and an AI that invents an unknown kind gets a suggestion in the 422 response rather than a cryptic enum error.
Catalog endpoint
GET /v1/automations/catalogReturns the full self-describing catalog the builder and external clients use to stay in sync:
{
"node_kinds": [
{
"kind": "input",
"description": "Prompt the contact and capture their reply into run context or a contact custom field",
"config_schema": { /* JSON Schema */ },
"port_derivation": "captured / invalid / timeout / skip"
},
{ "kind": "message", "…": "…" }
],
"entrypoint_kinds": [
{ "kind": "keyword", "channels": ["instagram", "facebook", "whatsapp", "telegram"], "config_schema": { … } },
{ "kind": "webhook_inbound", "…": "…" }
],
"binding_types": [ … ],
"action_types": [ … ],
"channel_capabilities": { "instagram": { "buttons": true, "quick_replies": 13, "gallery": true, … } },
"template_kinds": [
{ "kind": "comment_to_dm", "channels": ["instagram", "facebook"], "config_schema": { … } }
]
}The response is ETag-cached — agents can cache the catalog once per session and re-use it across many create/update calls. An MCP client typically fetches it at session start and treats subsequent enum validation entirely from memory.
Validation errors
When the graph validator fails, the response is:
{
"error": {
"code": "validation_failed",
"message": "Graph has 2 errors",
"details": [
{ "node_key": "ask_email", "code": "unknown_node_kind", "message": "Unknown node kind 'user_input_email'. Did you mean 'input'?" },
{ "edge_index": 2, "code": "edge_target_missing", "message": "Edge references unknown node 'tag_lad'. Did you mean 'tag_lead'?" }
]
}
}unknown_node_kind, unknown_port_key, duplicate_node_key, and edge_target_missing all return suggestion hints when a near-match is available. The agent retries with the corrected value rather than doing a blind second attempt.
If the graph is invalid, the server still saves it and forces status = "paused" — the agent can inspect the stored graph and fix it in place. Activation (POST /v1/automations/{id}/activate) re-runs the validator and returns 422 if errors remain.
Templates for one-shot flows
Templates collapse the common cases to a single call. Eight template kinds ship in v1 (blank, welcome_flow, faq_bot, lead_capture, comment_to_dm, story_leads, follower_growth, follow_to_dm). An agent looking at /v1/automations/catalog → template_kinds[*].config_schema can pick the right template and fill its config in one pass. See Templates for the catalog and config shapes.
Port-based edges
An AI generating a graph from scratch connects nodes via explicit (from_node, from_port, to_node, to_port) tuples:
{
"edges": [
{ "from_node": "greet", "from_port": "button.btn_large", "to_node": "order_large", "to_port": "in" },
{ "from_node": "ask_email", "from_port": "captured", "to_node": "tag_lead", "to_port": "in" },
{ "from_node": "ask_email", "from_port": "timeout", "to_node": "ask_email_retry", "to_port": "in" }
]
}No labels to guess. Every node kind's port keys are listed in the catalog, and ports on message / action_group nodes derive deterministically from config — so an agent that sets blocks[0].buttons[0].id = "btn_large" knows the port will be button.btn_large without asking the server.
SDK surface
The TypeScript SDK at packages/sdk/src/resources/automations.ts mirrors the REST API exactly:
relay.automations.*— CRUD, lifecycle (activate,pause,resume,archive),enroll,simulate,insights,catalogrelay.automationEntrypoints.*— entrypoint CRUD +rotateSecretrelay.automationBindings.*— binding CRUDrelay.automationRuns.*— runs, steps,stoprelay.contacts.automationControls.*— contact pause/resume
The full OpenAPI spec is exported via bun run --filter api export-openapi and served at /openapi.json + /docs on the running API.
Tips for prompting
- Prefer templates for one-shot asks. "Make a comment-to-DM flow on this post with keyword 'info'" → single
POST /v1/automationswithtemplate.kind = "comment_to_dm". - Give the agent the channel. Channel constrains which blocks and entrypoints are valid — surfacing it upfront avoids invalid-config 422 loops.
- Inspect before activating. Create with
status: "draft", fetch the generated graph, confirm it matches the user's intent, thenPOST /v1/automations/{id}/activate. - Use
simulateas a verifier.POST /v1/automations/{id}/simulateruns the graph without side effects — the returned transcript is a cheap way for an agent to confirm its own construction before going live.
AI nodes
AI-powered nodes (ai_step, ai_agent, ai_intent_router) are not available in v1. They were dropped from the Manychat-parity rebuild and are archived for a later release. If you need AI behaviour inside a flow today, use an http_request node to call your own LLM endpoint and branch on the response via success / error.
Found something wrong? Help us improve this page.