> ## Documentation Index
> Fetch the complete documentation index at: https://docs.connectly.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Send Campaigns

> Trigger one or more campaign flows to multiple recipients with variables, versioning, and per-entry status reporting 📩

## Endpoint

```json theme={null}
POST https://api.connectly.ai/v1/businesses/{businessId}/send/campaigns
```

| Parameter    | Location | Description                 |
| ------------ | -------- | --------------------------- |
| `businessId` | Path     | Your Connectly business ID. |
| `X-API-Key`  | Header   | Your API key.               |

<Tip>
  Campaigns must be published in the Connectly Flow Builder before you call this endpoint. Sending to an unpublished campaign returns a `409` error.
</Tip>

***

## Request body

```json theme={null}
{
  "entries": [...],
  "options": {...}
}
```

### `entries` array (required)

Each object in `entries` represents one recipient. You can mix entries for different campaigns in the same request.

| Field             | Type   | Required | Description                                                                                                                               |
| ----------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `client`          | string | Yes      | Recipient's WhatsApp number in E.164 format (e.g. `+16505551234`), or a BSUID-prefixed identifier (e.g. `bsuid:US.13491208655302741918`). |
| `campaignName`    | string | Yes      | Exact campaign name copied from the Flow Builder.                                                                                         |
| `variables`       | object | No       | Key-value pairs substituted into the flow's variable placeholders for this recipient.                                                     |
| `campaignVersion` | string | No       | Specific campaign version to target. If omitted, resolved by `options.if_version_unspecified`.                                            |
| `scheduledAt`     | string | No       | ISO 8601 datetime for future delivery (e.g. `"2024-03-15T10:00:00Z"`). Alpha — contact your Account Manager before using.                 |

### `options` object (optional)

| Field                            | Values                         | Description                                                                                                                                                             |
| -------------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `if_version_unspecified`         | `reuse_last_active`            | How to resolve the version when `campaignVersion` is not set. `reuse_last_active` targets the latest published version (default).                                       |
| `if_duplicate_check_unspecified` | `allow_one` / `allow_multiple` | `allow_one` (default) prevents sending the same campaign to the same customer twice. Set `allow_multiple` to override this — for example, for recurring service alerts. |

***

## Examples

<AccordionGroup>
  <Accordion title="Minimal entry — no variables">
    The smallest valid entry. Use this for campaigns that have no variable placeholders.

    ```json theme={null}
    {
      "entries": [
        {
          "client": "+16505551236",
          "campaignName": "campaign_basic"
        }
      ]
    }
    ```
  </Accordion>

  <Accordion title="Entry with variables">
    Variables are defined in the campaign flow in the Connectly UI. Pass the same key names here.

    ```json theme={null}
    {
      "entries": [
        {
          "client": "+16505551237",
          "campaignName": "campaign_with_variables",
          "variables": {
            "username": "Jane Doe",
            "product": "Smart Watch"
          }
        }
      ]
    }
    ```
  </Accordion>

  <Accordion title="Multiple campaigns in one request">
    Connectly creates a separate sendout for each unique `campaignName` + `campaignVersion` combination. This request produces three sendouts: `campaign_A` at `v1.0`, `campaign_A` at its latest active version, and `campaign_B` at `v1.0`.

    ```json theme={null}
    {
      "entries": [
        {
          "client": "+14155558234",
          "campaignName": "campaign_A",
          "campaignVersion": "v1.0",
          "variables": { "key1": "value1", "key2": "value2" }
        },
        {
          "client": "+14155559345",
          "campaignName": "campaign_A",
          "variables": { "key1": "value3", "key2": "value4" }
        },
        {
          "client": "+14155550456",
          "campaignName": "campaign_B",
          "campaignVersion": "v1.0",
          "variables": { "Date": "2024-03-15", "Name": "Alice", "Price": "$99" }
        }
      ],
      "options": {
        "if_version_unspecified": "reuse_last_active"
      }
    }
    ```
  </Accordion>

  <Accordion title="Allow duplicate sends">
    By default, the API prevents sending the same campaign to the same customer twice. Override with `allow_multiple` for use cases like recurring alerts.

    ```json theme={null}
    {
      "entries": [
        {
          "client": "+14155558234",
          "campaignName": "service_alert",
          "variables": { "incident": "Scheduled maintenance" }
        }
      ],
      "options": {
        "if_duplicate_check_unspecified": "allow_multiple"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Response

The API always returns HTTP `200` with a `data` array. Each element corresponds to a unique `campaignName` + `campaignVersion` sendout generated by the request.

| Field             | Type           | Description                                            |
| ----------------- | -------------- | ------------------------------------------------------ |
| `campaignId`      | string         | Unique identifier for the campaign.                    |
| `campaignName`    | string         | Name of the campaign.                                  |
| `campaignVersion` | string         | Version used for this sendout.                         |
| `sendoutId`       | string         | Unique identifier for the sendout.                     |
| `status`          | string         | `created`, `updated`, or `error`.                      |
| `acceptedCount`   | integer        | Number of entries accepted into the sendout.           |
| `rejectedCount`   | integer        | Number of entries rejected.                            |
| `error`           | object \| null | Error detail if `status` is `error`, otherwise `null`. |

<Note>
  If you call the API again for the same campaign and recipient group, `status` returns `"updated"` rather than `"created"` — no duplicate sendout is created.
</Note>

**Success response**

```json theme={null}
{
  "data": [
    {
      "campaignId": "2963626c-90ea-43e5-9b66-4ce70f003fe3",
      "campaignName": "campaign_basic",
      "campaignVersion": "018c5c51-8631-28b5-3c81-b70ecf14faef",
      "sendoutId": "183e801b-1438-4177-b283-909135096e69",
      "status": "created",
      "acceptedCount": 1,
      "rejectedCount": 0,
      "error": null
    }
  ]
}
```

***

## Error responses

Entry-level errors appear inside the `data` array with `status: "error"` — they don't cause the whole request to fail. HTTP-level errors indicate request-wide failures.

| HTTP status | Meaning                                                                    |
| ----------- | -------------------------------------------------------------------------- |
| `400`       | Request body is malformed or an entry is invalid.                          |
| `401`       | Missing or invalid API key.                                                |
| `404`       | One or more referenced campaigns do not exist.                             |
| `409`       | Campaign is not in a state that accepts sendouts (e.g. not yet published). |
| `429`       | Rate limit exceeded.                                                       |
| `500`       | Internal server error.                                                     |

<AccordionGroup>
  <Accordion title="Campaign not found">
    ```json theme={null}
    {
      "data": [
        {
          "campaignId": null,
          "campaignName": "wrong_name",
          "campaignVersion": null,
          "sendoutId": null,
          "status": "error",
          "acceptedCount": 0,
          "rejectedCount": 1,
          "error": {
            "message": "Campaign not found",
            "type": "ERROR_TYPE_NOT_FOUND",
            "code": "ERROR_CODE_CAMPAIGN_NOT_FOUND",
            "userTitle": "Campaign not found",
            "userMessage": "Please review the campaign name and/or campaign id."
          }
        }
      ]
    }
    ```
  </Accordion>

  <Accordion title="Campaign version not found">
    ```json theme={null}
    {
      "data": [
        {
          "campaignId": "2963626c-90ea-43e5-9b66-4ce70f003fe3",
          "campaignName": "campaign_basic",
          "campaignVersion": "018c4a55-f3df-580c-1629-602f3b14d190",
          "sendoutId": null,
          "status": "error",
          "acceptedCount": 0,
          "rejectedCount": 1,
          "error": {
            "message": "Campaign version not found",
            "type": "ERROR_TYPE_NOT_FOUND",
            "code": "ERROR_CODE_CAMPAIGN_VERSION_NOT_FOUND",
            "userTitle": "Campaign version not found",
            "userMessage": "Please review the campaign version."
          }
        }
      ]
    }
    ```
  </Accordion>

  <Accordion title="Missing or invalid variables">
    Triggered when required flow variables are absent or the entry payload is otherwise invalid.

    ```json theme={null}
    {
      "data": [
        {
          "campaignId": "2963626c-90ea-43e5-9b66-4ce70f003fe3",
          "campaignName": "campaign_with_variables",
          "campaignVersion": "018c5c51-8631-28b5-3c81-b70ecf14faef",
          "sendoutId": "183e801b-1438-4177-b283-909135096e69",
          "status": "error",
          "acceptedCount": 0,
          "rejectedCount": 1,
          "error": {
            "message": "Campaign entry is invalid",
            "type": "ERROR_TYPE_INVALID_REQUEST",
            "code": "ERROR_CODE_CAMPAIGN_ENTRY_INVALID",
            "userTitle": "Campaign entry is invalid",
            "userMessage": "Please check the inputs to the campaign entry."
          }
        }
      ]
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Rate limiting

This endpoint is limited to **200 requests per second**. Exceeding this returns HTTP `429 Too Many Requests`. Use exponential backoff in your client if you expect high-volume sendouts.
