Sending SMS
Send an SMS with one POST to /messages. Ponitor answers straight away with the message, already charged and queued for the network. The delivery report comes later, by webhook.
You need
- An API key with the Messaging API turned on. See API keys and base URLs.
- A sender:
PSandboxor1234in the sandbox, or your own approved Sender ID or Short Code in live. See Sender IDs and Short Codes.
Send the message
curl -X POST $PONITOR_BASE_URL/messaging/v1/messages \
-H "apiKey: $PONITOR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sender": "ACME",
"channel": "SMS",
"recipient": "+231770000000",
"externalId": "otp-58213",
"text": "Your Acme code is 482910. It expires in 5 minutes."
}'
| Field | What to send |
|---|---|
sender | Your Sender ID or Short Code, exactly as approved. |
channel | SMS. |
recipient | An Orange or Lonestar Cell MTN number, in international format: +231770000000 or 231770000000. |
externalId | Your own reference, unique within your team, such as an order number. See Retries and externalId. |
text | The message. |
Every field is described in Send a message.
Read the response
201 Created means the message is charged and queued for the network:
{
"message": {
"id": "e2c38865-dc0a-4299-baef-c07a9bfa9c33",
"channel": "SMS",
"direction": "OUTBOUND",
"sender": "ACME",
"recipient": "+231770000000",
"text": "Your Acme code is 482910. It expires in 5 minutes.",
"status": "QUEUED",
"cost": 0.05,
"externalId": "otp-58213",
"createdAt": "2026-10-05T09:30:00.112Z"
}
}
Store the id. Webhooks and Get a message use it. The message usually reaches SENT within seconds and DELIVERED soon after. See Delivery statuses.
200 OK instead means you already used this externalId. The body is the original message. Nothing new was sent or charged.
When a send is refused
| Status | Why | Fix |
|---|---|---|
400 | A field is missing or invalid. The body has errors, one per problem. | Fix the request, and send it with the same externalId. |
400 | The sender isn't yours, or isn't approved yet. | Use an approved sender. In the sandbox, use PSandbox or 1234. |
400 | Your balance can't cover the message: Insufficient account balance. Top up to continue sending. | Add funds under Billing. In the sandbox, set your test balance. |
401 | The key is wrong, or it doesn't match the base URL. | See API keys and base URLs. |
403 | The Messaging API isn't turned on, or your team isn't approved. | Check Subscribed APIs and Settings › Compliance. |
500 | Something failed on our side. | Send it again with the same externalId. |
Nothing is charged for a refused send.
Length and parts
The phone shows a long message as one, but the networks carry it in parts, and each part is charged.
| Your text | Characters per part |
|---|---|
| Plain letters, digits and common symbols (GSM-7) | 160 |
| Anything with an emoji, a curly quote or a letter outside GSM-7 (Unicode) | 70 |
One emoji makes the whole message Unicode, so a 100-character message with one emoji is two parts.
Leave out the year unless the date is more than six months away. Leave out links: they rarely work on feature phones, and they use up characters.
Send to a group
Each request sends one message to one number. To send the same message to a contact group or to a file of numbers, use SMS › Campaigns in the app.