# POST - Batch send

Sends messages to multiple recipients in a single API request. You will need to prepare a CSV file and upload it to this endpoint.

```
POST /campaigns/:campaignId/batch/messages
```

***

**Path Parameters**

| Parameter    | Type   | Required | Description                                                                                                   |
| ------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------- |
| `campaignId` | string | Yes      | The ID of the campaign to send messages through. Found in the Postman admin portal after creating a campaign. |

***

**Request Headers**

| Header          | Value                 | Required |
| --------------- | --------------------- | -------- |
| `Authorization` | `Bearer YOUR_API_KEY` | Yes      |
| `Content-Type`  | `multipart/form-data` | Yes      |

***

**Request Body**

Upload your CSV file as a `multipart/form-data` request. The form field name must be `file`.

**CSV format**

The CSV must include `recipient` and `language` as the first two columns, followed by columns for each template parameter defined in the campaign's message template.

```csv
recipient,language,recipient_name,topic
6599999999,english,Emily Yeo,passport application #12345F
6599999998,chinese,James Tan,passport application #67890A
```

**CSV requirements**

| Requirement            | Detail                                                                                                                |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Header row             | Must match the template parameter names exactly (lowercase letters, numbers, and `_` only).                           |
| `recipient` column     | Phone number with country code, without the `+` prefix. E.g. `6591234567`.                                            |
| `language` column      | Must be one of: `english`, `chinese`, `malay`, `tamil`. Must match a language configured for the campaign.            |
| Template parameters    | All template parameters must be filled for every row.                                                                 |
| Max file size          | 40 MB.                                                                                                                |
| Test environment limit | 20 rows maximum if you are in the test environment.                                                                   |
| Row errors             | Any errors in the CSV rows will cause the entire file upload to fail. You must fix all errors before uploading again. |

**Using a single `{{body}}` variable**

If you manage message templates within your own system, you may use a single `{{body}}` variable.

```csv
recipient,language,body
6599999999,english,"Fill in your system constructed message here"
6599999998,english,"Another message here"
```

**Line breaks in CSV**

Use keyboard line breaks directly within the CSV cell (not `\n`). Excel will automatically wrap the content in double quotes when saving as CSV.

```csv
recipient,language,body
6591234567,english,"Dear Amy
Your appointment for VACCINATION is confirmed.
Please do not reply to this message."
6599999999,english,"Dear John
Your appointment for VACCINATION is confirmed.
Please do not reply to this message."
```

Do not manually add double quotes around the `{{body}}` value if you are using Excel, as Excel adds them automatically when saving as CSV.

***

**Response**

**HTTP 201 Created**

Returns a validation result with the batch ID.

```json
{
  "isValid": true,
  "batchId": "<YOUR_BATCH_ID>"
}
```

| Field     | Type    | Description                                                                                                   |
| --------- | ------- | ------------------------------------------------------------------------------------------------------------- |
| `isValid` | boolean | Whether the CSV was valid and the batch was created.                                                          |
| `batchId` | string  | The unique batch ID. Use this to query the Retrieve Batch Messages endpoint or the Retry Batch Send endpoint. |

***

**Error Responses**

**HTTP 400 Bad Request**

| Error Code          | Message                                     | Cause                                                                                         |
| ------------------- | ------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `parameter_invalid` | File too large, max file size is 40MB       | CSV file exceeds the 40 MB size limit.                                                        |
| `parameter_invalid` | Validation failed (expected type is object) | CSV headers do not match the campaign's template parameters, or required columns are missing. |

**HTTP 401 Unauthorized**

| Error Code                 | Message                                          | Cause                                           |
| -------------------------- | ------------------------------------------------ | ----------------------------------------------- |
| `invalid_api_key_provided` | The API key provided is invalid.                 | API key is incorrect, expired, or deleted.      |
| `invalid_ip_address_error` | The IP address used for this request is invalid. | Request sent from a non-whitelisted IP address. |

**HTTP 429 Too Many Requests**

| Error Code          | Message           | Cause                                                                                                                      |
| ------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `too_many_requests` | Too many requests | Rate limit exceeded. Default is 10 TPS per campaign, shared across all endpoints. Implement exponential backoff and retry. |

***

**Example: cURL**

```bash
curl -X POST \
  https://postman.gov.sg/api/v2/campaigns/<YOUR_CAMPAIGN_ID>/batch/messages \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@/path/to/your/file.csv"
```

***

**Notes**

{% hint style="warning" %}
If your campaign sends more than 200,000 recipients or makes more than 50 API requests per second (or more than 100 message segments per second), you must submit the [large campaign form](https://form.gov.sg/67a17d1adcc3e09f3a56003a) to ensure other large campaigns are not also happening on the same day. Otherwise, delivery for all campaigns will be affected.
{% endhint %}

* For batch send, 1 TPS = 1 API call = multiple messages. If your CSV has 20 rows, one API call sends 20 messages. This differs from single send, where 1 TPS = 1 API call = 1 message.
* OTP messages should always use Single Send.
* The response confirms the batch was created, but does not indicate delivery status. Use the Retrieve Batch Messages endpoint to check the delivery status of individual messages in the batch.
* To retry failed messages in a batch, use the Retry Batch Send endpoint.
