---
title: "Create an outreach cadence"
method: POST
path: "/cadences"
tags: ["Cadences"]
---

# Create an outreach cadence

`POST /cadences`

## Request

- Base URL: `https://anjal.app`
- URL: `https://anjal.app/cadences`
- Auth: API key in header `X-API-Key`

A cadence is a server-side outreach sequence — an ordered list of
steps (LinkedIn invite → wait → DM → email → ...) that fire
automatically per enrollment with branching on real-world events
(invite accepted, lead replied).

The runtime is on Anjal's side. Customer apps call
`POST /v1/cadences/:id/enrollments` to enroll a lead and receive
webhook events as the cadence progresses.

## Request body

- object
  - `name` string, required
  - `description` string, nullable
  - `businessHoursOnly` boolean
  - `exitOnReply` boolean
  - `exitOnUnsubscribe` boolean
  - `dailySendCap` integer
  - `webhookUrl` string, uri, nullable
  - `steps` CadenceStepInput[]: Optional inline step list. You can also PATCH /v1/cadences/:id/steps later.
    - `kind` 'linkedin_connect' | 'linkedin_message' | 'email' | 'wait', required
    - `delayDays` number, required: Days after the previous step's completion. 0 = fire on enrollment for step 0; 0.25 = 6 hours.
    - `condition` 'after_accept' | 'no_reply_to_prev' | 'never_replied', nullable
    - `draftStrategy` 'static' | 'webhook': How the runner gets the rendered content for this step. - `static` — Use `staticBody` (+ `staticSubject` for email) as the message verbatim, with `{{name}}`, `{{first_name}}`, `{{email}}` token substitution. Predictable; zero external deps. - `webhook` — At fire-time, the runner POSTs a `cadence.step.draft.requested` event to the cadence's `webhookUrl` and waits up to 30s for a JSON response of `{ body, subject?, skip?, defer? }`. The customer's app brings the actual content with its own business context, its own LLM if it wants one, its own thread memory. Anjal never generates copy on the customer's behalf. The legacy `"ai"` value was REMOVED 2026-05-19 — Anjal no longer drafts content server-side. New cadences passing `"ai"` get a `VALIDATION_ERROR` with a migration hint pointing here. Pre-existing rows with `"ai"` will surface `AI_STRATEGY_REMOVED` as the per-enrollment `lastError.code` when the runner tries to fire them — fix by updating the step to `static` (with `staticBody`) or `webhook` (with the cadence's `webhookUrl` set).
    - `staticSubject` string, nullable: Email-only. The literal subject line for static-strategy email steps. Supports `{{name}}` / `{{first_name}}` / `{{email}}` tokens.
    - `staticBody` string, nullable: The literal message body for static-strategy steps. Supports `{{name}}` / `{{first_name}}` / `{{email}}` tokens. Required when `draftStrategy: "static"`.
    - `promptHint` string, nullable: Free-text hint for the step. Anjal does not read this — it's passed through verbatim in the `cadence.step.draft.requested` webhook payload (`step.promptHint`) so the customer's draft handler can use it as per-step context. Ignored by static strategy.
    - `notes` string, nullable: Operator notes — not sent anywhere, just stored for the customer's reference.

## Response `201`

Cadence created.

- object
  - `ok` boolean
  - `cadence` Cadence
    - `id` string, uuid
    - `name` string
    - `description` string, nullable
    - `enabled` boolean
    - `businessHoursOnly` boolean
    - `exitOnReply` boolean
    - `exitOnUnsubscribe` boolean
    - `dailySendCap` integer
    - `webhookUrl` string, nullable
    - `webhookSecret` string, nullable: HMAC secret for X-MyAgentMail-Signature.
    - `createdAt` string, date-time
    - `updatedAt` string, date-time
    - `steps` CadenceStep[], nullable
      - `kind` 'linkedin_connect' | 'linkedin_message' | 'email' | 'wait', required
      - `delayDays` number, required: Days after the previous step's completion. 0 = fire on enrollment for step 0; 0.25 = 6 hours.
      - `condition` 'after_accept' | 'no_reply_to_prev' | 'never_replied', nullable
      - `draftStrategy` 'static' | 'webhook': How the runner gets the rendered content for this step. - `static` — Use `staticBody` (+ `staticSubject` for email) as the message verbatim, with `{{name}}`, `{{first_name}}`, `{{email}}` token substitution. Predictable; zero external deps. - `webhook` — At fire-time, the runner POSTs a `cadence.step.draft.requested` event to the cadence's `webhookUrl` and waits up to 30s for a JSON response of `{ body, subject?, skip?, defer? }`. The customer's app brings the actual content with its own business context, its own LLM if it wants one, its own thread memory. Anjal never generates copy on the customer's behalf. The legacy `"ai"` value was REMOVED 2026-05-19 — Anjal no longer drafts content server-side. New cadences passing `"ai"` get a `VALIDATION_ERROR` with a migration hint pointing here. Pre-existing rows with `"ai"` will surface `AI_STRATEGY_REMOVED` as the per-enrollment `lastError.code` when the runner tries to fire them — fix by updating the step to `static` (with `staticBody`) or `webhook` (with the cadence's `webhookUrl` set).
      - `staticSubject` string, nullable: Email-only. The literal subject line for static-strategy email steps. Supports `{{name}}` / `{{first_name}}` / `{{email}}` tokens.
      - `staticBody` string, nullable: The literal message body for static-strategy steps. Supports `{{name}}` / `{{first_name}}` / `{{email}}` tokens. Required when `draftStrategy: "static"`.
      - `promptHint` string, nullable: Free-text hint for the step. Anjal does not read this — it's passed through verbatim in the `cadence.step.draft.requested` webhook payload (`step.promptHint`) so the customer's draft handler can use it as per-step context. Ignored by static strategy.
      - `notes` string, nullable: Operator notes — not sent anywhere, just stored for the customer's reference.
      - `id` string, uuid
      - `cadenceId` string, uuid
      - `stepIndex` integer

---

[API](https://skmtc.dev/anjal/apis/anjal-api.md) · [All operations](https://skmtc.dev/anjal/apis/anjal-api/llms.txt) · [OpenAPI document](https://skmtc.dev/anjal/apis/anjal-api/revisions/a4044778e294?raw)
