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. 12 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 stable vocabulary and channel capability catalog the builder and external clients use to stay in sync. Detailed request shapes remain in OpenAPI and the node, trigger, binding, and template guides.
{
"node_kinds": [
{
"kind": "input",
"label": "Input",
"category": "content",
"description": "Wait for a user reply and capture it into the run context"
},
{ "kind": "message", "…": "…" }
],
"entrypoint_kinds": [
{ "kind": "dm_received", "label": "DM Received", "channels": ["instagram", "facebook", "whatsapp", "telegram"] },
{ "kind": "webhook_inbound", "…": "…" }
],
"binding_types": [ … ],
"action_types": [ … ],
"channel_capabilities": { "instagram": { "buttons": true, "quick_replies": true, "quick_replies_max": 13, "gallery": true, … } },
"template_kinds": [
"blank", "welcome_flow", "faq_bot", "lead_capture", "comment_to_dm", "story_leads", "follower_growth", "follow_to_dm"
]
}The response is ETag-cached — agents can cache the catalog once per session and re-use it across many create/update calls. Use the OpenAPI document for exact field validation and this catalog for deployed vocabulary and channel availability.
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). Use /v1/automations/catalog to confirm a kind is deployed, then use Templates or OpenAPI for its strict config shape.
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. Port keys are documented in Nodes, 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.