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

# Create Batch Outbound Calls

> Queue multiple outbound calls in a single API request

## API Endpoint

```bash theme={null}
POST https://app.sawt.sa/api/v1/calls/outbound
```

## Request Headers

<ParamField header="Authorization" required>
  Bearer your-api-key
</ParamField>

<ParamField header="Content-Type" required>
  application/json
</ParamField>

## Request Body

<ParamField body="agentId" type="string" required>
  The ID of the outbound agent to use for all calls in the batch.
</ParamField>

<ParamField body="calls" type="array" required>
  List of calls to queue. Minimum 1, maximum 100 per request. Each item must include `phoneNumber` and optionally `promptVariables` and `metadata`.
</ParamField>

<ParamField body="calls[].phoneNumber" type="string" required>
  The phone number to call (e.g., `"966501234567"`). Must match your SIP trunk's outbound format.
</ParamField>

<ParamField body="calls[].promptVariables" type="object">
  Dynamic variables injected into the agent's prompt for this specific call. Key-value pairs of strings.
</ParamField>

<ParamField body="calls[].metadata" type="object">
  Arbitrary metadata attached to this call. Not passed to the agent prompt.
</ParamField>

<Accordion title="Example Request">
  ```json theme={null}
  {
    "agentId": "e63355c6-cf51-40f3-b006-b615d9ab762d",
    "calls": [
      {
        "phoneNumber": "966501234567",
        "promptVariables": {
          "customer_name": "Ahmed Al-Rashid",
          "appointment_date": "Wednesday, October 8, 2025",
          "appointment_time": "3:00 PM"
        }
      },
      {
        "phoneNumber": "966509876543",
        "promptVariables": {
          "customer_name": "Sara Al-Otaibi",
          "appointment_date": "Thursday, October 9, 2025",
          "appointment_time": "11:00 AM"
        }
      },
      {
        "phoneNumber": "966551112233",
        "promptVariables": {
          "customer_name": "Khalid Al-Harbi",
          "appointment_date": "Friday, October 10, 2025",
          "appointment_time": "1:00 PM"
        }
      }
    ]
  }
  ```
</Accordion>

## Response

<ResponseField name="success" type="boolean" required>
  `true` if at least one call was queued successfully.
</ResponseField>

<ResponseField name="data.policy" type="object" required>
  The duplicate handling policy applied to this batch.
</ResponseField>

<ResponseField name="data.policy.mode" type="string" required>
  `DEDUPE` — duplicate phone numbers within the same request are dropped. `SPACED` — duplicates are allowed but spaced 10 minutes apart.
</ResponseField>

<ResponseField name="data.policy.spacingMinutes" type="number" required>
  Minutes between duplicate calls when mode is `SPACED`. Always `10`.
</ResponseField>

<ResponseField name="data.summary" type="object" required>
  Counts for the outcome of each call in the batch.
</ResponseField>

<ResponseField name="data.summary.received" type="number" required>
  Total number of calls received in the request.
</ResponseField>

<ResponseField name="data.summary.queuedNow" type="number" required>
  Calls queued for immediate dialing.
</ResponseField>

<ResponseField name="data.summary.queuedLater" type="number" required>
  Calls queued with a future scheduled time (only applies in `SPACED` mode for duplicates).
</ResponseField>

<ResponseField name="data.summary.deduped" type="number" required>
  Calls dropped because the same phone number already appeared earlier in the request (only in `DEDUPE` mode).
</ResponseField>

<ResponseField name="data.summary.failed" type="number" required>
  Calls that failed validation or hit a rate limit.
</ResponseField>

<ResponseField name="data.items" type="array" required>
  Per-call outcome list. One entry per call in the original request.
</ResponseField>

<ResponseField name="data.items[].phoneNumber" type="string" required>
  The phone number from the original request.
</ResponseField>

<ResponseField name="data.items[].action" type="string" required>
  One of: `queued_now`, `queued_later`, `failed`, `deduped`.
</ResponseField>

<ResponseField name="data.items[].scheduledFor" type="string">
  ISO timestamp of when the call will be dialed. Only present when `action` is `queued_later`.
</ResponseField>

<ResponseField name="data.items[].reason" type="string">
  Failure or dedup reason. Only present when `action` is `failed` or `deduped`.
</ResponseField>

<ResponseField name="data.webhookId" type="string" required>
  Webhook ID for tracking this batch's call events.
</ResponseField>

<ResponseField name="data.agentId" type="string" required>
  The agent ID used for the batch.
</ResponseField>

<ResponseField name="data.total" type="number" required>
  Total calls received (same as `summary.received`).
</ResponseField>

<ResponseField name="data.queued" type="number" required>
  Total calls successfully queued (`queuedNow + queuedLater`).
</ResponseField>

<ResponseField name="data.failed" type="number" required>
  Total calls that failed.
</ResponseField>

<ResponseField name="data.duplicatesRemoved" type="number" required>
  Number of duplicate phone numbers removed in `DEDUPE` mode.
</ResponseField>

<Accordion title="Example Response">
  ```json theme={null}
  {
    "success": true,
    "data": {
      "policy": {
        "mode": "DEDUPE",
        "spacingMinutes": 10
      },
      "summary": {
        "received": 3,
        "queuedNow": 3,
        "queuedLater": 0,
        "deduped": 0,
        "failed": 0
      },
      "items": [
        { "phoneNumber": "966501234567", "action": "queued_now" },
        { "phoneNumber": "966509876543", "action": "queued_now" },
        { "phoneNumber": "966551112233", "action": "queued_now" }
      ],
      "webhookId": "webhook_xyz789",
      "agentId": "e63355c6-cf51-40f3-b006-b615d9ab762d",
      "total": 3,
      "queued": 3,
      "failed": 0,
      "duplicatesRemoved": 0
    }
  }
  ```
</Accordion>

## Duplicate Handling

The duplicate policy is configured per company in the dashboard. The default is `DEDUPE`.

**`DEDUPE` (default):** If the same phone number appears more than once in a single request, only the first occurrence is queued. The rest are dropped with `action: "deduped"`.

**`SPACED`:** All occurrences are queued but spaced 10 minutes apart. Later duplicates will have `action: "queued_later"` with a `scheduledFor` timestamp.

## Error Handling

<ResponseField name="error" type="string">
  Top-level error message when the entire request fails (e.g., agent not found, missing SIP trunk).
</ResponseField>

Per-call failures are returned in `data.items` with `action: "failed"` and a `reason` field — they do not cause the whole request to fail.

### Common Errors

<Warning>
  * **`"phoneNumber" or "calls" must be provided, not both`** — do not mix single-call and batch fields in the same request
  * **`promptVariables/metadata must be per-call for batch requests`** — top-level `promptVariables` is not allowed for batch; put them inside each call item
  * **Agent not found / not configured** — agent must be an outbound agent with a SIP trunk
  * **Contact rate limit** — a phone number was called too recently; `data.items[].reason` will include the reset time
</Warning>

<Accordion title="Example Partial Failure Response">
  ```json theme={null}
  {
    "success": true,
    "data": {
      "policy": { "mode": "DEDUPE", "spacingMinutes": 10 },
      "summary": {
        "received": 3,
        "queuedNow": 2,
        "queuedLater": 0,
        "deduped": 0,
        "failed": 1
      },
      "items": [
        { "phoneNumber": "966501234567", "action": "queued_now" },
        { "phoneNumber": "966509876543", "action": "failed", "reason": "Contact rate limit exceeded. Resets at 2025-10-08T15:00:00Z" },
        { "phoneNumber": "966551112233", "action": "queued_now" }
      ],
      "webhookId": "webhook_xyz789",
      "agentId": "e63355c6-cf51-40f3-b006-b615d9ab762d",
      "total": 3,
      "queued": 2,
      "failed": 1,
      "duplicatesRemoved": 0
    }
  }
  ```
</Accordion>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://app.sawt.sa/api/v1/calls/outbound" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer your-api-key" \
    -d '{
      "agentId": "e63355c6-cf51-40f3-b006-b615d9ab762d",
      "calls": [
        {
          "phoneNumber": "966501234567",
          "promptVariables": {
            "customer_name": "Ahmed Al-Rashid",
            "appointment_date": "Wednesday, October 8, 2025",
            "appointment_time": "3:00 PM"
          }
        },
        {
          "phoneNumber": "966509876543",
          "promptVariables": {
            "customer_name": "Sara Al-Otaibi",
            "appointment_date": "Thursday, October 9, 2025",
            "appointment_time": "11:00 AM"
          }
        },
        {
          "phoneNumber": "966551112233",
          "promptVariables": {
            "customer_name": "Khalid Al-Harbi",
            "appointment_date": "Friday, October 10, 2025",
            "appointment_time": "1:00 PM"
          }
        }
      ]
    }'
  ```

  ```javascript Node.js theme={null}
  const axios = require("axios");

  const createBatchCalls = async () => {
    try {
      const response = await axios.post(
        "https://app.sawt.sa/api/v1/calls/outbound",
        {
          agentId: "e63355c6-cf51-40f3-b006-b615d9ab762d",
          calls: [
            {
              phoneNumber: "966501234567",
              promptVariables: {
                customer_name: "Ahmed Al-Rashid",
                appointment_date: "Wednesday, October 8, 2025",
                appointment_time: "3:00 PM",
              },
            },
            {
              phoneNumber: "966509876543",
              promptVariables: {
                customer_name: "Sara Al-Otaibi",
                appointment_date: "Thursday, October 9, 2025",
                appointment_time: "11:00 AM",
              },
            },
            {
              phoneNumber: "966551112233",
              promptVariables: {
                customer_name: "Khalid Al-Harbi",
                appointment_date: "Friday, October 10, 2025",
                appointment_time: "1:00 PM",
              },
            },
          ],
        },
        {
          headers: {
            Authorization: "Bearer your-api-key",
            "Content-Type": "application/json",
          },
        }
      );

      console.log(response.data);
      return response.data;
    } catch (error) {
      console.error(error);
    }
  };

  createBatchCalls();
  ```

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

  def create_batch_calls():
      url = "https://app.sawt.sa/api/v1/calls/outbound"

      headers = {
          "Authorization": "Bearer your-api-key",
          "Content-Type": "application/json"
      }

      data = {
          "agentId": "e63355c6-cf51-40f3-b006-b615d9ab762d",
          "calls": [
              {
                  "phoneNumber": "966501234567",
                  "promptVariables": {
                      "customer_name": "Ahmed Al-Rashid",
                      "appointment_date": "Wednesday, October 8, 2025",
                      "appointment_time": "3:00 PM"
                  }
              },
              {
                  "phoneNumber": "966509876543",
                  "promptVariables": {
                      "customer_name": "Sara Al-Otaibi",
                      "appointment_date": "Thursday, October 9, 2025",
                      "appointment_time": "11:00 AM"
                  }
              },
              {
                  "phoneNumber": "966551112233",
                  "promptVariables": {
                      "customer_name": "Khalid Al-Harbi",
                      "appointment_date": "Friday, October 10, 2025",
                      "appointment_time": "1:00 PM"
                  }
              }
          ]
      }

      try:
          response = requests.post(url, headers=headers, json=data)
          response.raise_for_status()
          return response.json()
      except requests.exceptions.RequestException as e:
          print(f"Error: {e}")
          return None

  result = create_batch_calls()
  print(result)
  ```
</RequestExample>

## Webhooks

<Card title="Real-time Call Events" icon="webhook" href="/api-reference/endpoint/webhooks">
  Subscribe to webhooks to receive real-time notifications for each call in the batch. All calls in a batch share the same `webhookId`.
</Card>
