> For the complete documentation index, see [llms.txt](https://postman-v2.guides.gov.sg/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://postman-v2.guides.gov.sg/technical-users-api/api-reference/post-single-send.md).

# POST - Single send

{% hint style="danger" %}
**Do not use the test environment to send messages to the public.**
{% endhint %}

If you are sending time-sensitive, critical SMSes like OTPs or weather alerts, please use the single send API.

The response on whether the message was created will come in immediately. However, you will need to query the Retrieve a Single Message endpoint to get the message `latestStatus`.

```
POST /campaigns/<campaignId>/messages
```

***

**Path Parameters**

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

***

**Request Headers**

| Header          | Value                 | Required |
| --------------- | --------------------- | -------- |
| `Authorization` | `Bearer YOUR_API_KEY` | Yes      |
| `Content-Type`  | `application/json`    | Yes      |

***

**Request Body**

| Parameter   | Type   | Required | Description                                                                                                                                                                                            |
| ----------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `recipient` | string | Yes      | The recipient's phone number with country code, without the `+` prefix. E.g. `6591234567` for a Singapore number.                                                                                      |
| `language`  | string | Yes      | The language of the message template. Possible values: `english`, `chinese`, `malay`, `tamil`. Must match one of the languages configured for the campaign.                                            |
| `values`    | object | Yes      | An object containing key-value pairs for the campaign's template parameters. The keys must match the `{{variables}}` defined in the campaign's message template. All values must be non-empty strings. |

**Example request body**

```json
{
  "recipient": "6599999999",
  "language": "english",
  "values": {
    "name": "John Doe",
    "fruit": "apple"
  }
}
```

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

If you manage message templates within your own system and use Postman solely for sending, you may create a single `{{body}}` variable and insert the full message into it.

```json
{
  "recipient": "6599999999",
  "language": "english",
  "values": {
    "body": "Fill in your system constructed message here"
  }
}
```

**Line breaks**

To include line breaks in your message, add `\n` into the JSON request body. Make sure your request body is in valid JSON format.

```json
{
  "recipient": "6599999999",
  "language": "english",
  "values": {
    "body": "Dear John,\n\nYour appointment is confirmed.\n\nPlease do not reply to this message."
  }
}
```

***

**Response**

**HTTP 201 Created**

Returns the created message object.

```json
{
  "createdAt": "2024-01-29T17:39:35.574+08:00",
  "updatedAt": "2024-01-29T17:39:35.574+08:00",
  "id": "<YOUR_GENERATED_MESSAGE_ID>",
  "recipient": "6599999999",
  "values": {
    "name": "John Doe",
    "fruit": "apple"
  },
  "fullMessage": "<YOUR_FULL_MESSAGE>",
  "latestStatus": "created",
  "templateBodyId": "<YOUR_TEMPLATE_BODY_ID>",
  "campaignId": "<YOUR_CAMPAIGN_ID>",
  "language": "english",
  "creatorId": "<USER_ID_OF_MESSAGE_CREATOR>"
}
```

| Field            | Type   | Description                                                                                                                      |
| ---------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `createdAt`      | string | ISO 8601 timestamp of when the message was created.                                                                              |
| `updatedAt`      | string | ISO 8601 timestamp of the last update to the message.                                                                            |
| `id`             | string | The unique message ID. Use this to query the Retrieve a Single Message endpoint.                                                 |
| `recipient`      | string | The recipient's phone number.                                                                                                    |
| `values`         | object | The template parameter values used in the message.                                                                               |
| `fullMessage`    | string | The full rendered message text, including Postman's header and footer.                                                           |
| `latestStatus`   | string | The current delivery status of the message. Will be `created` on initial response. See Message Statuses for all possible values. |
| `templateBodyId` | string | The ID of the template body used.                                                                                                |
| `campaignId`     | string | The campaign ID the message belongs to.                                                                                          |
| `language`       | string | The language used for the message.                                                                                               |
| `creatorId`      | string | The user ID of the message creator.                                                                                              |

***

**Error Responses**

**HTTP 400 Bad Request**

| Error Code          | Message                                                                             | Cause                                                                                 |
| ------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `parameter_invalid` | Invalid recipient, ensure it is a valid normalized phone number, eg 6591112222.     | Phone number format is incorrect.                                                     |
| `parameter_invalid` | \[language] is not supported. available language(s) for this campaign: \[languages] | Language not configured for this campaign.                                            |
| `parameter_invalid` | Validation failed (expected type is object)                                         | Template variables missing or not provided as an object with non-empty string values. |

**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>/messages \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "recipient": "6599999999",
    "language": "english",
    "values": {
      "name": "John Doe",
      "fruit": "apple"
    }
  }'
```

***

**Notes**

* The `latestStatus` in the response will always be `created`. You must poll the Retrieve a Single Message endpoint to get the final delivery status (`success` or `failure`).
* Webhooks for delivery status updates are not currently supported.
* Postman does not automatically retry failed messages. Use the Retry Single Send endpoint to retry failed messages manually.
* For single send, 1 TPS = 1 API call = 1 message. This differs from batch send, where 1 TPS = 1 API call = multiple messages. Use batch send to avoid hitting rate limits.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://postman-v2.guides.gov.sg/technical-users-api/api-reference/post-single-send.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
