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

# Create campaign

> Create a new outbound WhatsApp broadcast campaign for immediate or scheduled dispatch

Create a new outbound campaign for your organization. If `scheduled_at` is omitted or in the past, sending begins immediately via background workers. If `scheduled_at` is in the future, the campaign is scheduled via TaskIQ and stored with status `scheduled`.

***

### Authentication

This endpoint requires Bearer token authentication.

```http theme={null}
Authorization: Bearer <token>
```

| Header          | Type     | Required | Description                                     | Format           |
| :-------------- | :------- | :------- | :---------------------------------------------- | :--------------- |
| `Authorization` | `string` | **Yes**  | Scoped organization API key or Bearer JWT token | `Bearer <token>` |

***

### Input parameters

The following parameters are accepted in the JSON request body.

<ParamField header="Authorization" type="string" required>
  Bearer token for authentication. Format: `Bearer <token>`.
</ParamField>

<ParamField body="name" type="string" required>
  Descriptive title of the campaign (maximum 255 characters). Example: `Summer Sale Launch`.
</ParamField>

<ParamField body="whatsapp_phone_number_id" type="string" required>
  UUID of the registered WhatsApp Business phone number configured for your organization.
</ParamField>

<ParamField body="message_template_id" type="string">
  UUID of the approved WhatsApp message template to broadcast.
</ParamField>

<ParamField body="audience_type" type="string" default="all">
  Audience resolution strategy:

  * `all`: Broadcasts to all contacts registered in the organization.
  * `tags`: Filters contacts matching any tags specified in `audience_tags`.
  * `manual`: Dispatches only to the contacts listed in `audience_contact_ids`.
</ParamField>

<ParamField body="audience_tags" type="string[]">
  Array of contact tags to target when `audience_type` is set to `tags`. Example: `["vip", "q3_leads"]`.
</ParamField>

<ParamField body="audience_contact_ids" type="string[]">
  Array of contact UUIDs to target when `audience_type` is set to `manual`.
</ParamField>

<ParamField body="scheduled_at" type="string">
  ISO 8601 timestamp (e.g. `2026-09-15T14:30:00Z`) for future dispatch. Leave empty to begin dispatching immediately.
</ParamField>

***

### Response Fields

<ResponseField name="id" type="string">
  Generated UUID identifier of the campaign.
</ResponseField>

<ResponseField name="organization_id" type="string">
  UUID of the owning organization.
</ResponseField>

<ResponseField name="name" type="string">
  Campaign name.
</ResponseField>

<ResponseField name="status" type="string">
  Initial status of the campaign (`sending` for immediate dispatch, or `scheduled` for future delivery).
</ResponseField>

<ResponseField name="whatsapp_phone_number_id" type="string">
  UUID of the sending WhatsApp phone number channel.
</ResponseField>

<ResponseField name="message_template_id" type="string">
  UUID of the message template, or `null`.
</ResponseField>

<ResponseField name="audience_type" type="string">
  Targeting strategy (`all`, `tags`, or `manual`).
</ResponseField>

<ResponseField name="audience_tags" type="string[]">
  Array of targeted tags, or `null`.
</ResponseField>

<ResponseField name="audience_contact_ids" type="string[]">
  Array of targeted contact IDs, or `null`.
</ResponseField>

<ResponseField name="scheduled_at" type="string">
  ISO 8601 scheduled dispatch timestamp, or `null`.
</ResponseField>

<ResponseField name="total_contacts_count" type="number">
  Total number of recipient contacts resolved for this campaign.
</ResponseField>

<ResponseField name="sent_count" type="number">
  Count of messages dispatched.
</ResponseField>

<ResponseField name="delivered_count" type="number">
  Count of messages confirmed delivered.
</ResponseField>

<ResponseField name="read_count" type="number">
  Count of messages marked read by recipients.
</ResponseField>

<ResponseField name="failed_count" type="number">
  Count of messages that failed delivery.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 creation timestamp.
</ResponseField>

<ResponseField name="updated_at" type="string">
  ISO 8601 last modification timestamp.
</ResponseField>

***

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.sayvy.ai/api/v1/campaigns" \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Q3 Lead Reactivation",
      "whatsapp_phone_number_id": "8f3b0e2a-71d3-4a5f-9e6b-123456789abc",
      "message_template_id": "3a4b5c6d-7e8f-9012-3456-789abcdef012",
      "audience_type": "tags",
      "audience_tags": ["high_intent", "q3_leads"],
      "scheduled_at": "2026-09-15T14:00:00Z"
    }'
  ```

  ```python Python theme={null}
  import requests

  url = "https://api.sayvy.ai/api/v1/campaigns"

  payload = {
      "name": "Q3 Lead Reactivation",
      "whatsapp_phone_number_id": "8f3b0e2a-71d3-4a5f-9e6b-123456789abc",
      "message_template_id": "3a4b5c6d-7e8f-9012-3456-789abcdef012",
      "audience_type": "tags",
      "audience_tags": ["high_intent", "q3_leads"],
      "scheduled_at": "2026-09-15T14:00:00Z"
  }

  headers = {
      "Authorization": "Bearer <token>",
      "Content-Type": "application/json"
  }

  response = requests.post(url, json=payload, headers=headers)
  print(response.status_code)
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://api.sayvy.ai/api/v1/campaigns", {
    method: "POST",
    headers: {
      "Authorization": "Bearer <token>",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      name: "Q3 Lead Reactivation",
      whatsapp_phone_number_id: "8f3b0e2a-71d3-4a5f-9e6b-123456789abc",
      message_template_id: "3a4b5c6d-7e8f-9012-3456-789abcdef012",
      audience_type: "tags",
      audience_tags: ["high_intent", "q3_leads"],
      scheduled_at: "2026-09-15T14:00:00Z"
    })
  });

  const data = await response.json();
  console.log(data);
  ```

  ```java Java theme={null}
  HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://api.sayvy.ai/api/v1/campaigns"))
      .header("Authorization", "Bearer <token>")
      .header("Content-Type", "application/json")
      .POST(HttpRequest.BodyPublishers.ofString("""
        {
          "name": "Q3 Lead Reactivation",
          "whatsapp_phone_number_id": "8f3b0e2a-71d3-4a5f-9e6b-123456789abc",
          "message_template_id": "3a4b5c6d-7e8f-9012-3456-789abcdef012",
          "audience_type": "tags",
          "audience_tags": ["high_intent", "q3_leads"],
          "scheduled_at": "2026-09-15T14:00:00Z"
        }
      """))
      .build();

  HttpResponse<String> response =
      HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
  System.out.println(response.body());
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Created theme={null}
  {
    "id": "e4a2e584-3672-4d69-b541-6e9f1a238491",
    "organization_id": "93f35dc2-5d46-4e58-a9ee-038c35d9bcf1",
    "name": "Q3 Lead Reactivation",
    "whatsapp_phone_number_id": "8f3b0e2a-71d3-4a5f-9e6b-123456789abc",
    "message_template_id": "3a4b5c6d-7e8f-9012-3456-789abcdef012",
    "audience_type": "tags",
    "audience_tags": [
      "high_intent",
      "q3_leads"
    ],
    "audience_contact_ids": null,
    "scheduled_at": "2026-09-15T14:00:00Z",
    "status": "scheduled",
    "total_contacts_count": 0,
    "sent_count": 0,
    "delivered_count": 0,
    "read_count": 0,
    "failed_count": 0,
    "created_at": "2026-09-07T10:30:00Z",
    "updated_at": "2026-09-07T10:30:00Z"
  }
  ```

  ```json 400 Bad Request theme={null}
  {
    "detail": "WhatsApp channel/phone number not found for this organization."
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "detail": "Unauthorized"
  }
  ```

  ```json 429 Too Many Requests theme={null}
  {
    "error": "rate_limit_exceeded",
    "message": "Rate limit exceeded for endpoint. Please retry after backoff.",
    "retry_after": 60
  }
  ```
</ResponseExample>

***

<div
  style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
backgroundColor: "rgba(255, 255, 255, 0.03)",
border: "1px solid rgba(255, 255, 255, 0.08)",
borderRadius: "16px",
padding: "10px 18px",
marginTop: "40px",
gap: "16px",
flexWrap: "wrap"
}}
>
  <a
    href="/api-reference/campaigns/overview"
    style={{
display: "inline-flex",
alignItems: "center",
gap: "6px",
color: "#94A3B8",
textDecoration: "none",
fontSize: "14px",
fontWeight: "500",
padding: "4px 8px"
}}
  >
    <span style={{ fontSize: "16px" }}>‹</span> Previous
  </a>

  <div
    style={{
display: "flex",
alignItems: "center",
gap: "16px",
backgroundColor: "rgba(255, 255, 255, 0.04)",
border: "1px solid rgba(255, 255, 255, 0.06)",
borderRadius: "12px",
padding: "8px 16px",
marginLeft: "auto"
}}
  >
    <div style={{ textAlign: "right" }}>
      <div style={{ fontSize: "13px", fontWeight: "700", color: "#F8FAFC" }}>List campaigns</div>

      <div style={{ fontSize: "11px", color: "#94A3B8", maxWidth: "260px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
        GET /api/v1/campaigns
      </div>
    </div>

    <div style={{ width: "1px", height: "24px", backgroundColor: "rgba(255, 255, 255, 0.1)" }} />

    <a
      href="/api-reference/campaigns/list-campaigns"
      style={{
  display: "inline-flex",
  alignItems: "center",
  gap: "6px",
  color: "#94A3B8",
  textDecoration: "none",
  fontSize: "14px",
  fontWeight: "500"
}}
    >
      Next <span style={{ fontSize: "16px" }}>›</span>
    </a>
  </div>
</div>
