---
title: "Create campaign"
method: POST
path: "/v1/campaigns/create"
tags: ["campaigns"]
---

# Create campaign

`POST /v1/campaigns/create`

## Request

- Base URL: `https://api.zevari.ai`
- URL: `https://api.zevari.ai/v1/campaigns/create`
- Auth: HTTP bearer

Transport: call this endpoint with HTTP POST and a JSON request body. Create a draft campaign. This does not start outbound sending. Public REST campaign creation does not require a launch-campaign skill_run_id; the skill receipt remains an MCP creation requirement. Enrollment defaults to enrollment_mode='fixed'. Explicitly set enrollment_mode='continuous' when the campaign must accept future targets while active. Send enrollment_mode as a TOP-LEVEL field, not inside settings — a nested value is ignored, the campaign silently defaults to fixed, and the response returns a warning. A continuous campaign still needs executable shared campaign-level content at creation; after activation, campaigns.addTargets can admit new recipients with their own approval-bound personalized_steps snapshots. enrollment_mode='continuous' is now supported together with settings.personalization_mode='personalized': later recipients can each carry their own per-lead sequence (a distinct ordered set of steps) in addition to per-lead copy. This is additive and backward-compatible. The response returns status='draft', effective enrollment_mode, targets_editable, add_targets_method='campaigns.addTargets', and next_action so a human or agent can continue without relying on external docs. To launch the draft, call confirmations.requestAction with action_type='campaign_activate' and payload={campaign_id}, then call campaigns.updateStatus with status='active' and the approved confirmation_id.

## Request body

- object
  - `name` string, required: Campaign name
  - `campaign_type` 'warm_up' | 'outreach' | 'content' | 'custom', required: Campaign type
  - `goal` string: Campaign goal (e.g. book_meeting, start_conversation). Falls back to user's default CTA goal.
  - `goal_custom` string: Custom goal text when goal is 'other'
  - `resource_url` string: Resource URL for campaign CTA (e.g., calendar link)
  - `settings` object: Campaign settings (working_hours, timezone, stop_on_reply, etc.)
  - `skill_run_id` string: Required for MCP-created campaigns. Call skills_get with slug='launch-campaign' first and pass the returned skill_run_id.
  - `enrollment_mode` 'fixed' | 'continuous': Recipient policy. Send this as a TOP-LEVEL parameter — a value nested inside `settings` is ignored and the campaign silently defaults to 'fixed'. Omit for the default 'fixed', which preserves the approved target set. 'continuous' authorizes one-call additions while active; new targets are validated and scheduled without pausing existing execution.
  - `steps` object[], required: Ordered list of campaign steps
    - `action_type` 'view_profile' | 'like_post' | 'comment' | 'connect' | 'message' | 'react_to_post' | 'email_send', required
    - `delay_hours` number
    - `config` object: Step config. Outbound fields per action_type: connect→message (optional, ≤300 chars) or messages{slug:text}; message→body (required) or messages{slug:text}; comment→comment_text (required) or messages{slug:text}. For new campaigns, LinkedIn per-target maps MUST be keyed by exact linkedin_provider_id (ACoAAA...), and email per-target maps MUST be keyed by exact lead_email. POST DESTINATIONS (canonical): for like_post / react_to_post / comment, set config.post_refs as { "<linkedin_provider_id>": { "id": "<numeric_activity_id>" } }. Use { "*": { "id": "..." } } for one global post across all targets. For connect/message that reference a post, use config.context_post_refs with the same shape. Without post_refs, set config.post_resolution = "dynamic" to opt into most-recent-post resolution. For a threaded reply on a specific post, set config.comment_id (requires the post_refs entry to be that post). DO NOT submit config.post_id, config.post_url, config.context_post_id, or config.context_post_url as object maps — the server returns an invalid_post_destination_shape error pointing to post_refs. email_send→subject/subjects + body/messages. Omit from_address unless the user explicitly provided one; Zevari resolves the verified sender. Use config.instruction for internal notes — NEVER config.note (rejected).
  - `targets` object[], required: Targets to enroll in the campaign
    - `identity` string: Who to add: a LinkedIn profile URL, public slug (e.g. 'satyanadella'), provider id (ACoAA...), or email address. Zevari classifies and resolves it server-side — preferred over the legacy per-type fields.
    - `lead_id` string
    - `linkedin_provider_id` string
    - `name` string
    - `lead_name` string
    - `url` string
    - `lead_url` string
    - `linkedin_profile_url` string
    - `profile_url` string
    - `email` string, email
    - `lead_email` string, email
    - `work_email` string, email
    - `template_slug` string
    - `template_variant` string
    - `sequence` object[]: Optional per-lead sequence. When set, this lead runs these steps instead of the campaign-global steps.
      - `action_type` 'view_profile' | 'like_post' | 'comment' | 'connect' | 'message' | 'react_to_post' | 'email_send', required
      - `delay_hours` number
      - `config` object: Literal step config for this lead. Outbound copy goes here directly: connect->message (optional, <=300 chars); message->body (required); comment->comment_text (required); email_send->subject + body. Post destinations use config.post_refs (see campaign step config). config.instruction for internal notes; config.note is rejected.

## Response `200`

Draft campaign created. The response describes how to add targets and what to do next.

- ZevariResponseEnvelope
  - `ok` boolean, required: False when the underlying tool reports an error or a partial failure.
  - `method` string, required: Public API method name.
  - `data` unknown, required
  - `request_id` string, required: Request correlation ID. Also returned in X-Zevari-Request-Id.

## Other responses

- `400`: Invalid campaign definition, including missing executable shared content for a continuous campaign at creation, or another method execution error.
- `401`: Missing, invalid, expired, revoked, or unauthorized API key.
- `404`: Unknown or non-public Zevari API method.
- `429`: API key rate limit exceeded.
- `500`: Unhandled server error.

---

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