---
title: "Add campaign targets"
method: POST
path: "/v1/campaigns/addTargets"
tags: ["campaigns"]
---

# Add campaign targets

`POST /v1/campaigns/addTargets`

## Request

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

Transport: call this endpoint with HTTP POST and a JSON request body. Add recipients to an existing campaign. Active fixed and continuous campaigns can admit new recipients with personalized_steps without pausing or reactivation. Existing recipients and shared campaign steps remain unchanged.

Required for personalized enrollment: a stable idempotency_key (match the Idempotency-Key header when used) and complete personalized_steps covering every content-bearing step_order.

Approval: stages campaign_enroll_personalized_targets. Eligible Autopilot applies and schedules in the same call; otherwise show approval_url, wait, then repeat the identical request with confirmation_id.

Idempotency: Same key and same payload replays; Same key with changed recipients or copy returns IDEMPOTENCY_KEY_CONFLICT; PERSONALIZED_ENROLLMENT_CONFIRMATION_REJECTED is terminal and a new key requires a new user request.

Response: approved copy is stored as immutable personalized_snapshot_v1 snapshots; read content_mode, scheduling_occurred, and each disposition's scheduled, schedule_deferred, and schedule_defer_reason.

Recovery: PERSONALIZED_SEQUENCE_INCOMPLETE (missing step copy); CAMPAIGN_PERSONALIZED_SNAPSHOT_INVALID (a draft/paused campaign's approved step structure drifted); active fixed generic additions require pause, add, validate, fresh campaign_activate approval, and reactivation; terminal campaigns require a new campaign.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `campaign_id` string, required: Campaign ID to enroll targets into
  - `targets` object[]: Leads to add — identify each with `identity` (URL, slug, provider id, or email). Active campaigns accept one-call additions; unresolved or duplicate leads come back itemized instead of failing the batch. Add an optional per-lead `sequence` for custom steps/copy. Omit targets entirely when retrying with a confirmation_id: Zevari executes the approved batch it already stored.
    - `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.
    - `personalized_steps` object[]: DEPRECATED — use `sequence` instead. Legacy per-step copy keyed by the campaign's existing step_order; still accepted for older callers.
      - `step_order` integer, required: Existing campaign step order
      - `message` string, nullable: Exact connection note; null means an intentional no-note invite
      - `body` string: Exact LinkedIn message or email body
      - `subject` string: Exact email subject
      - `comment_text` string: Exact LinkedIn comment
  - `idempotency_key` string: Stable caller-generated key for safe retries. Reusing the key with the same batch returns the original result; a key whose previous attempt was REJECTED may be reused with corrected content.
  - `confirmation_id` string: Approval for action_type='campaign_enroll_personalized_targets'. Omit on the first personalized call: Zevari stages the approval. After approval, retry with this ID and the same idempotency_key — targets may be omitted; the stored approved batch is executed.

## Response `200`

Enrollment accepted, scheduled, or replayed idempotently. Inspect next_action and per-target dispositions.

- 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 enrollment, personalized content or approval failure, idempotency conflict, terminal campaign, or other 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)
