Create a campaign

View as Markdown
Creates a campaign in `draft` status, bound to an agent, a channel and a weekly calling window. Nothing is sent or dialed until you add contacts and call `PATCH /api/campaigns/{campaignId}/activate`. A WhatsApp template or email content can be included now, so the campaign is ready to send in one request. These checks run before the campaign is saved, in this order: - the agent exists in this workspace (404 `AGENT_NOT_FOUND`); - the agent serves the campaign's `channel` (400); - for `channel: email`, the workspace has its own sending mailbox connected **and** selected as sender (409); - template placeholder maps and `{{token}}`s in the email subject and body resolve (400); - the WhatsApp variable map only uses placeholders the template body has (400). `maxConcurrentCalls` is silently lowered to the plan's ceiling (Starter 3, Growth 10, Business 25), and the stored value is what you get back. The WhatsApp template's approval status is **not** checked here; activation checks it. **Side effects.** Writes the campaign row and an audit entry. No provider is called and no contact is reached. **Idempotency.** Not idempotent: retrying after a timeout creates a second draft. List campaigns before retrying, or delete the duplicate with `DELETE /api/campaigns/{campaignId}`. **Webhook events.** `audit.log_recorded` for webhooks subscribed to it. See [Webhooks](/webhooks). **Access** - **Required scope:** `full`. Admin-only route: `read`/`write` keys get 403; signed-in users need the admin or owner role. - **Rate limit:** Configuration mutations — 10 requests/min per workspace, shared with agent mutations, on top of the general API limit (120 to 600 requests/min by plan). See [Rate limits](/rate-limits). - **Plan:** Counts against the plan's **active**-campaign cap (Starter 1, Growth 3, Business and Enterprise unlimited). The draft does not use a slot, but creation is refused with 403 `BILLING_ERROR` while every slot is taken, or when the workspace has no active plan.

Authentication

AuthorizationBearer
Workspace API key: `jl_` followed by 64 lowercase hex characters, created by the workspace owner in the dashboard (**Settings → API Keys**) and sent as `Authorization: Bearer jl_...`. The plaintext is shown once, at creation; Jelliu stores only a SHA-256 hash. A workspace can hold up to 25 active keys. | Scope | GET / HEAD | POST / PUT / PATCH / DELETE | Admin-only routes | | --- | --- | --- | --- | | `read` | Yes | No | No | | `write` | Yes | Yes | No | | `full` | Yes | Yes | Yes | Operations restricted to admins or owners reject keys without the `full` scope with `403`, and say so in their description. No key, whatever its scope, can mint or revoke API keys or rotate a webhook secret — that requires a signed-in owner session. A revoked key stops authenticating within about 10 seconds. See [Authentication](/authentication).

Request

This endpoint expects an object.
agentIdstringRequiredformat: "uuid"

Agent of this workspace. It must serve the campaign’s channel (400 otherwise); an unknown agent answers 404.

namestringRequired1-200 characters
Display name.
productContextstringRequired10-5000 characters
What the campaign is about, given to the agent. At least 10 characters after trimming.
targetAudiencestringRequired1-1000 characters
Who the contacts are, given to the agent.
scheduleobjectRequired

Weekly calling window, stored and returned exactly as sent (camelCase inside the object). The dialer starts a call only when the local hour in timezone is >= startHour and < endHour on an enabled day; outside it, the call is rescheduled to the next window. A call that started inside the window may run past endHour. Days that are left out count as disabled. Validation: timezone must be a valid IANA zone (Invalid IANA timezone), every enabled day needs startHour < endHour, and at least one day must be enabled (At least one day must be enabled). WhatsApp and email sends are not bound to this window.

maxConcurrentCallsintegerOptional1-500Defaults to 10

Maximum simultaneous calls. Silently lowered to the plan’s ceiling (Starter 3, Growth 10, Business 25) and to the platform maximum.

maxRetryAttemptsintegerOptional0-10Defaults to 3

Maximum dial attempts per contact, counting the first; unanswered, busy or unconnected calls are redialed until this many exist. 0 or 1 disables redialing. Voice only. A plan gate (403 BILLING_ERROR) exists for values above 0, but every plan includes retries today.

retryIntervalMinutesintegerOptional5-1440Defaults to 60
Minutes between a missed call and its redial.
channelenumOptionalDefaults to voice

voice, whatsapp, webchat or email (see Campaign.channel). An email campaign needs the workspace’s own mailbox connected in Integrations and selected as sender (409 otherwise).

categoryenumOptionalDefaults to sales

Purpose of the campaign, used to classify outcomes (see Campaign.category for each value).

whatsappTemplateIdstringOptionalformat: "uuid"

WhatsApp template (from the workspace’s templates) that makes activation send outbound messages. WhatsApp approval is checked at activation, not here.

whatsappTemplateVariableslist of strings or map from strings to stringsOptional

Campaign-wide placeholder values, the same for every contact. Either an array of up to 20 strings ({{1}} is index 0) or an object keyed by index (keys up to 8 characters); values up to 500 characters.

whatsappTemplateVariableMapmap from strings to strings or objectsOptional

Placeholders filled per contact, keyed by placeholder index (up to 3 characters), for example {"1": "contact.first_name"}. A value is the token alone, meaning skip a contact that cannot resolve it, or { token, fallback } to write the fallback instead. Skipped contacts end as invalid. Indexes must exist in the template body (400 otherwise).

emailSubjectstringOptional1-300 characters

Subject for outbound email, may contain tokens. Needs emailBody too; activation refuses one without the other.

emailBodystringOptional1-20000 characters

Plain-text body for outbound email, may contain tokens.

Response

The campaign was created in draft status.

dataobject

A campaign row as returned by the retrieve, create, update, activate and pause endpoints. Keys are snake_case, exactly as stored. The list endpoint returns a lighter CampaignListItem instead.

Errors

400
Bad Request Error
401
Unauthorized Error
403
Forbidden Error
404
Not Found Error
409
Conflict Error
429
Too Many Requests Error