curl --request POST \
--url https://brew.new/api/v1/automations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Welcome flow",
"description": "Welcome new signups",
"triggerEventId": "user_signed_up",
"nodes": [
{
"id": "trg",
"label": "On signup",
"type": "trigger",
"config": {
"actionType": "trigger"
}
},
{
"id": "send_welcome",
"label": "Welcome",
"type": "sendEmail",
"config": {
"actionType": "sendEmail",
"emailId": "V1StGXR8_Z5jdHi6B-myT",
"emailVersionId": "Ux8nW3qL1mP9zRv6TkYcB",
"domainId": "kx7bkh53hasmfeh5kd7sqgykt187g8ww",
"subject": "Welcome to Brew, {{firstName | there}}!",
"previewText": "Thanks for signing up — get started in 2 minutes.",
"fromName": "Brew",
"replyTo": "support@example.com"
}
}
],
"connections": [
{
"from": "trg",
"to": "send_welcome"
}
]
}
'{
"valid": false,
"blockers": [
{
"severity": "error",
"message": "Add at least one Send Email action"
}
],
"warnings": [],
"blockingIssues": [],
"nodeCounts": {
"sendEmail": 0,
"wait": 0,
"filter": 0,
"split": 0
}
}Create an automation
Deterministic create: the body carries the full graph ({ name, triggerEventId?, nodes, connections }).
Use when you have minted every design a sendEmail node references (generateEmail first). Publish afterwards with updateAutomation ({ "published": true }), or launch a manual-audience automation with runAutomation.
Input Trigger binding, exactly one: an EVENT automation passes triggerEventId; a MANUAL-AUDIENCE automation omits it and gives the trigger node config: { "mode": "manualAudience", "audienceId": "…" }. Typed conditions: every filter and condition-mode split uses a non-empty conditions array; each condition needs field, type (string, number, date or bool) and a canonical snake_case operator; unary operators omit value, comparisons require a type-correct scalar, a non-empty array, or an exact two-value between tuple (see AutomationNode). Every sendEmail node carries emailId, emailVersionId and subject (previewText is optional; the design’s <Preview> is the source of truth). domainId is optional at create time; publishing or running requires a verified one from listDomains. There is no messageClass key: the delivery class derives from the sending domain’s sendingPurpose (a transactional-purpose domain sends with no unsubscribe link and delivers to unsubscribed contacts), so pick the class by picking the domain. dryRun: true validates without persisting.
Returns 201 with the bare AutomationRow; with dryRun: true, 200 with an AutomationDryRunReport { valid, blockers[], warnings[], blockingIssues[], nodeCounts } (blockers[] fail publish, warnings[] are advisory, blockingIssues[] lists per-node references the bound trigger or contact catalog cannot provide, each of which also fails publish; valid is false when any blocker or blocking issue is present).
Errors 400 AUTOMATION_GRAPH_INVALID with details.issues[] (kind, nodeId, message per problem); 404 TRIGGER_EVENT_NOT_FOUND for an unknown triggerEventId.
See also updateAutomation, testAutomation, runAutomation, createTrigger.
curl --request POST \
--url https://brew.new/api/v1/automations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Welcome flow",
"description": "Welcome new signups",
"triggerEventId": "user_signed_up",
"nodes": [
{
"id": "trg",
"label": "On signup",
"type": "trigger",
"config": {
"actionType": "trigger"
}
},
{
"id": "send_welcome",
"label": "Welcome",
"type": "sendEmail",
"config": {
"actionType": "sendEmail",
"emailId": "V1StGXR8_Z5jdHi6B-myT",
"emailVersionId": "Ux8nW3qL1mP9zRv6TkYcB",
"domainId": "kx7bkh53hasmfeh5kd7sqgykt187g8ww",
"subject": "Welcome to Brew, {{firstName | there}}!",
"previewText": "Thanks for signing up — get started in 2 minutes.",
"fromName": "Brew",
"replyTo": "support@example.com"
}
}
],
"connections": [
{
"from": "trg",
"to": "send_welcome"
}
]
}
'{
"valid": false,
"blockers": [
{
"severity": "error",
"message": "Add at least one Send Email action"
}
],
"warnings": [],
"blockingIssues": [],
"nodeCounts": {
"sendEmail": 0,
"wait": 0,
"filter": 0,
"split": 0
}
}Authorizations
Send your Brew API key as Authorization: Bearer brew_xxx.
Headers
Optional idempotency key for safe retries. Reusing the same key with the same request body returns the original response for 24 hours.
1 - 100The brand this request acts on. REQUIRED for organization-scoped credentials (otherwise 400 BRAND_ID_REQUIRED — there is no default brand); list ids with GET /v1/brands. Brand-scoped credentials may omit it, and sending a different brand returns 403 BRAND_SCOPE_MISMATCH. A brand outside your organization returns 404 BRAND_NOT_FOUND.
1 - 64Body
1 - 120500- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
Show child attributes
Show child attributes
20001 - 2561000Show child attributes
Show child attributes
Validate only — runs the full publish-gate check and returns blockers/warnings without creating anything.
Response
Dry-run result (returned only for dryRun: true; no writes). valid is false when any blockers[] entry or any blockingIssues[] entry is present. Create and update both return blockingIssues[] (possibly empty).
Whether the graph would publish cleanly. false when any blockers[] entry or any blockingIssues[] entry is present.
Publish blockers (severity: "error"). Publishing fails while any remain.
Show child attributes
Show child attributes
Advisory findings (severity: "warning"). Publishing succeeds despite them.
Show child attributes
Show child attributes
Per-kind node counts of the validated graph.
Show child attributes
Show child attributes
Trigger-payload / draft-variable compatibility findings. Present (possibly empty) on POST and PATCH dry-run responses. Every entry blocks publish, and valid is false when any is present.
Show child attributes
Show child attributes
Was this page helpful?