---
title: "Add contacts to a campaign that is already running."
method: POST
path: "/campaigns/{id}/leads"
tags: ["campaigns"]
---

# Add contacts to a campaign that is already running.

`POST /campaigns/{id}/leads`

## Request

- Base URL: `https://api.coldrig.dev`
- URL: `https://api.coldrig.dev/campaigns/{id}/leads`
- Auth: HTTP bearer

Allowed only on an active or paused campaign of this account. Each new contact starts at step 1 on the campaign's own sequence and schedule, under every launch rule: addresses are trimmed and lower-cased, a {{firstName}} or {{key}} the sequence uses must have a value on every new contact (else 400 and nothing is added), and `listSource` is required on a crowdfunding account. Skipped and counted, never an error: an address already in the campaign (nothing about it changes), an address on your suppression list (stored as suppressed, never emailed), and an address repeated in the request. Adding never resumes a paused campaign, and repeating the call adds nothing. A campaign holds at most 5000 contacts in total. People who already got this email are skipped: a contact is left out, never stored or emailed, when another campaign of this account has sent them, or still has waiting for them, an email with the same subject and body as this campaign's first email, as written before {{tokens}} are filled in (spacing differences do not count). A trial's test email counts only while the account is still on the trial, and the built-in demo campaign never counts. alreadyEmailed in the answer is how many were skipped (anyone also on your suppression list is counted there, not in suppressed): tell the person in plain words, for example "skipped 12 people who already got this email". To include them, send includeAlreadyEmailed: true on the launch itself (previewOnly: true shows the count first), or after a launch add them to that same campaign with add_leads and includeAlreadyEmailed: true. Never launch the same list again to include them: that emails everyone who was not skipped a second time. Naming a repeat with repeatOf includes them too. A launch on which everyone would be skipped is refused (400) and nothing is created; an add of contacts on which everyone is skipped adds no one, with no error. A trial account can launch up to 5 test campaigns in any 24 hours, each holding up to 100 contacts, which is enough for a small test. Both are limits of the trial, not of Coldrig: going live removes them. Nothing in a campaign launched on the trial is ever really sent: when the account goes live, the emails it still has waiting are canceled and it cannot be resumed or added to, so launch a new campaign to send real email.

A launch, or an add of contacts, is refused with 409 duplicate_campaign when it would leave a campaign with exactly the same set of contact addresses and the same subjects and bodies in the same step order as another campaign of this account, however long ago that one was launched and whether it is running, paused, ended or finished, so the same people are not sent the same emails twice. `repeatOf` states a deliberate repeat, exactly as on `POST /campaigns`.

## Path parameters

- `id` string, required

## Request body

- object
  - `leads` LeadInput[], required
    - `email` string, email, required: Trimmed and lower-cased before it is checked or stored.
    - `firstName` string: Optional. A launch whose subject or body uses {{firstName}} is refused 400, naming how many contacts have none; no fallback word is invented. One line of plain text: a line break, tab or control character is refused (400) naming this field. Right-to-left text and its direction marks are accepted.
    - `company` string: One line of plain text: a line break, tab or control character is refused (400) naming this field. Right-to-left text and its direction marks are accepted.
    - `vars` object: Optional per-lead values for your own {{key}} tokens: word-keys (letters, digits, underscore) to one-line plain-text strings (no line breaks, tabs or control characters). Right-to-left text and its direction marks are accepted. A {{key}} used in any subject or body must be present and non-empty on EVERY lead, or the launch is refused 400 naming the token and how many leads lack it. A key may not be firstName or company. A value is inserted once as plain text; a value that itself contains {{...}} reaches the recipient as that literal text.
  - `listSource` string: Where these contacts came from, in a sentence. REQUIRED on a crowdfunding account (a missing or "unknown" source is refused 400); optional elsewhere.
  - `repeatOf` string: Optional. Send it only to repeat a campaign on purpose: the existingCampaignId that a 409 duplicate_campaign refusal named. Accepted once, and only while it names the most recent campaign of this account with the same contacts and the same subjects and bodies as written; any other value is refused and nothing is created.
  - `includeAlreadyEmailed` boolean: Optional. true also stores and emails the contacts who already got this campaign's first email from another campaign of this account; without it they are skipped and counted in alreadyEmailed.

## Response `200`

The contacts were processed; the counts say what happened to each.

- object
  - `campaignId` string, required
  - `added` integer, required: New contacts scheduled from step 1.
  - `suppressed` integer, required: New addresses on your suppression list: stored as suppressed, never emailed.
  - `alreadyInCampaign` integer, required: Addresses already in this campaign: skipped, unchanged.
  - `alreadyEmailed` integer, required: New addresses left out because they already got this campaign's first email from another campaign: never stored. 0 when includeAlreadyEmailed or repeatOf includes them.
  - `duplicatesRemoved` integer, required: Repeated addresses in the request
  - `contacts` integer, required: Contacts in the campaign after this call.
  - `simulation` TrialSimulation: What a trial account's launch, add-contacts or resume call played out for the campaign before it answered. Counts what that call played, not the campaign's totals. A paid account's answer has no `simulation`.
    - `sent` integer, required: Sends played out by this call.
    - `replies` integer, required: Replies produced by this call.
    - `bounces` integer, required: Bounces produced by this call.
    - `complaints` integer, required: Complaints produced by this call.
    - `waiting` integer, required: Sends of this campaign still waiting when the call answered; 0 means the whole sequence played out.
    - `reason` 'the rest is playing out by itself' | 'no mailbox on this account can send' | 'the campaign is paused' | 'the campaign's send window has no open hour' | 'nothing more could be sent': Present only when `waiting` is above zero. The rest finishes by itself without a further call only for `the rest is playing out by itself`; the other reasons say why nothing more could be played.

## Other responses

- `400`: Request body failed schema validation.
- `401`: Missing, invalid, or inactive bearer token.
- `404`: Resource not found, or not owned by the authenticated account.
- `409`: Refused: the add would make this campaign a repeat of another one. It carries `code: duplicate_campaign`, `existingCampaignId`, `existingCampaignName` and `existingCampaignState`, and nothing was added.

---

[API](https://skmtc.dev/coldrig/apis/agent-cold-email-api.md) · [All operations](https://skmtc.dev/coldrig/apis/agent-cold-email-api/llms.txt) · [OpenAPI document](https://skmtc.dev/coldrig/apis/agent-cold-email-api/revisions/242a462d82c4?raw)
