---
title: "Create an automation"
method: POST
path: "/v1/automations"
tags: ["Automations"]
---

# Create an automation

`POST /v1/automations`

Deterministic create — the body carries the full graph (`{ name, triggerEventId?, nodes, connections }`). Returns `201` with the bare `AutomationRow`.

**Trigger binding (exactly one):** for an EVENT automation pass `triggerEventId` (then publish with `PATCH … { "published": true }`). For a MANUAL-AUDIENCE automation OMIT `triggerEventId` and give the trigger node `config: { "mode": "manualAudience", "audienceId": "aud_…" }` — it is launched on demand with `POST /v1/automations/{automationId}/run` rather than published.

**Typed conditions:** every filter and condition-mode split uses a non-empty `conditions` array. Each condition requires `field`, `type` (`string`, `number`, `date`, or `bool`), and a canonical snake_case `operator`. Unary operators omit `value`; comparisons require a type-correct scalar, non-empty array, or exact two-value `between` tuple as documented by `AutomationNode`.

Chain `POST /v1/emails { prompt }` first to mint the design each `sendEmail` node references — every `sendEmail` node MUST carry `emailId`, `emailVersionId`, `domainId`, `subject`, `previewText`.

**Dry-run** — add `dryRun: true` to validate without persisting; returns `200` with `{ valid, blockers[], warnings[], blockingIssues[], nodeCounts }`. `blockingIssues[]` lists per-node references to a trigger-payload variable the bound trigger cannot provide (`{ nodeId, nodeLabel, surface, variable, reason, fatal }`) — e.g. after swapping the trigger or removing a payload field. `fatal: true` (filter/split conditions and triple-brace `{{{ }}}` body tokens) fails publish; `fatal: false` (subject/previewText/fromName/replyTo and double-brace body tags) renders empty at send time and is advisory only. `valid` is false when any `fatal` blocking issue is present.

## Headers

- `Idempotency-Key` string

## Request body

- AutomationsPostRequest
  - `name` string, required
  - `description` string
  - `triggerEventId` string
  - `nodes` union[], required
    - union
      - object
        - `id` string, required
        - `label` string, required
        - `description` string
        - `type` 'trigger', required
        - `config` object, required
          - `actionType` string
          - `mode` 'event' | 'manualAudience'
          - `triggerEventId` string
          - `audienceId` string
          - `eventName` string
      - object
        - `id` string, required
        - `label` string, required
        - `description` string
        - `type` 'sendEmail', required
        - `config` object, required
          - `actionType` string
          - `emailId` string, required
          - `emailVersionId` string, required
          - `domainId` string, required
          - `subject` string, required
          - `previewText` string, required
          - `messageClass` 'marketing' | 'transactional'
          - `fromName` string
          - `replyTo` string, email
          - `emailTitle` string
          - `fromAddress` string
      - object
        - `id` string, required
        - `label` string, required
        - `description` string
        - `type` 'wait', required
        - `config` object, required
          - `actionType` string
          - `duration` number, required
          - `unit` 'ms' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks', required
      - object
        - `id` string, required
        - `label` string, required
        - `description` string
        - `type` 'filter', required
        - `config` object, required
          - `actionType` string
          - `logicalOperator` 'AND' | 'OR', required
          - `conditions` union[], required
            - union
              - …
      - object
        - `id` string, required
        - `label` string, required
        - `description` string
        - `type` 'split', required
        - `config` union, required
          - object
            - `actionType` string
            - `mode` 'percentage', required
            - `leftLabel` string, required
            - `rightLabel` string, required
            - `leftPercentage` number, required
            - `seed` string
          - object
            - `actionType` string
            - `mode` 'condition', required
            - `leftLabel` string, required
            - `rightLabel` string, required
            - `logicalOperator` 'AND' | 'OR', required
            - `conditions` union[], required
              - …
  - `connections` object[]
    - `from` string, required
    - `to` string, required
    - `branch` 'left' | 'right'
  - `dryRun` boolean

## Response `200`

Dry-run result (no writes). `valid` indicates whether the graph would publish cleanly.

## Other responses

- `201` — Created. The bare automation row (draft, full graph).
- `400` — Strict-body violation OR the graph references designs / domains that do not exist or are not ready. `AUTOMATION_GRAPH_INVALID` includes `error.details.issues` with one entry per offending node / connection.
- `401` — The API key was missing, invalid, or revoked.
- `403` — The caller does not have the required `automations` permission.
- `404` — The referenced `triggerEventId` does not exist.
- `409` — The same `Idempotency-Key` was reused with a different request body.
- `429` — The request hit the rolling rate limit window.
- `500` — Unexpected internal error.

---

[API](https://skmtc.net/brew/apis/brew-public-api-v1.md) · [All operations](https://skmtc.net/brew/apis/brew-public-api-v1/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/brew/brew-public-api-v1/versions/db96cfedc6c5/schema)
