Send a Message
Send an SMS or MMS to a single recipient.
Endpoint
POST /api/v1/messaging/message
Request body
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Recipient phone number in E.164 format (e.g., +18005551234) |
from | string | Yes | Sender phone number in E.164 format or short code (5-6 digits). Must be an active number assigned to your account. |
body | string | Conditional | Message text, up to 640 characters. Required for SMS. Optional for MMS if mediaUrl is provided. |
mediaUrl | string | No | Publicly accessible URL to a media file. When provided, the message is sent as MMS. |
idempotencyKey | string | No | Unique key to prevent duplicate sends on retries. See Idempotency. |
SMS example
curl -X POST https://platform.textingline.com/api/v1/messaging/message \
-u "your_key_id:your_key_secret" \
-H "Content-Type: application/json" \
-d '{
"to": "+18005551234",
"from": "+18005559876",
"body": "Your verification code is 482910"
}'MMS example
curl -X POST https://platform.textingline.com/api/v1/messaging/message \
-u "your_key_id:your_key_secret" \
-H "Content-Type: application/json" \
-d '{
"to": "+18005551234",
"from": "+18005559876",
"body": "Check out our new collection!",
"mediaUrl": "https://example.com/images/promo.jpg"
}'Response
{
"id": "msg_DsAdb4EBYBh3z25sBA1qnv",
"direction": "outbound",
"to": "+18005551234",
"from": "+18005559876",
"body": "Your verification code is 482910",
"status": "sent",
"sentAt": "2026-03-15T14:30:05.000Z",
"createdAt": "2026-03-15T14:30:00.000Z"
}| Field | Description |
|---|---|
id | Unique message identifier, prefixed with msg_ |
direction | Message direction: outbound or inbound |
status | One of sent, queued, or failed |
errorType | Error classification. Present only when status is failed. See values below. |
errorMessage | Human-readable error description. Present only when status is failed. |
sentAt | ISO 8601 timestamp of when the message was sent to the provider. Absent when status is failed. |
createdAt | ISO 8601 timestamp of when the message was created |
Merge tags
The body field supports merge tags like {{contact_first_name}} and {{contact_custom_field_1}}. If the recipient's phone number matches a contact in your account's contact book, the platform substitutes the contact's values before the message is sent.
curl -X POST https://platform.textingline.com/api/v1/messaging/message \
-u "your_key_id:your_key_secret" \
-H "Content-Type: application/json" \
-d '{
"to": "+18005551234",
"from": "+18005559876",
"body": "Hi {{contact_first_name}}, your appointment is confirmed for 3 PM."
}'If +18005551234 has a stored firstName of Alice, the recipient sees Hi Alice, your appointment is confirmed for 3 PM..
Missing data resolves to an empty string — no error is raised. If no contact exists for the recipient's phone number, OR a contact exists but the referenced field is empty (e.g. the contact has no firstName), the variable resolves to an empty string and the recipient sees Hi , your appointment is confirmed for 3 PM.. The message is sent in both cases.
Merge tags are substituted in body only — mediaUrl is not templated. Putting {{contact_first_name}} in mediaUrl sends a literal request to that URL with the braces intact (and likely 404s).
See Merge Tags for the full variable list (contact-scoped, custom fields, date helpers, tracking links) and design guidance for templated bodies.
Recipient compliance checks
Before a single send is forwarded to the provider, the recipient runs through the same compliance gate used by broadcasts and batches:
- Opt-in filter — if the recipient is not opted in to any group owned by your account, the request fails with error code
33028. - Hard opt-out filter — if the recipient has explicitly refused messages (texted STOP, opted out at a kiosk, or was flagged via carrier propagation), the request fails with error code
33029. This is a permanent refusal; re-engaging requires fresh consent from the contact themselves.
Both errors return 400 Bad Request and prevent the message from being queued or charged.
Failed responses
When the message is accepted but the provider cannot deliver it (e.g., the recipient has opted out at the provider level), the API returns a 201 response with status: failed instead of an HTTP error. This allows you to distinguish between request-level errors (4xx/5xx) and delivery-level failures.
{
"id": "msg_Hp2sABcDeFgHiJkLmNoPqR",
"direction": "outbound",
"to": "+18005551234",
"from": "+18005559876",
"body": "Your verification code is 482910",
"status": "failed",
"errorType": "opted_out",
"errorMessage": "The recipient has opted out from receiving messages from this sender.",
"createdAt": "2026-03-15T14:30:00.000Z"
}Error types
See Error Codes — Message error types for the full list of errorType values and their descriptions.
Credits
Each message consumes credits from your account balance:
- SMS: 2 credits (1 message + 1 carrier lookup)
- MMS: 2 SMS credits + 1 MMS credit
If your balance is insufficient, the request is rejected with error code 33023. Check your credit balance in the dashboard.
Rate limits
The send message endpoint allows 60 requests per minute. See Rate Limits for details.
Common errors
| Code | Description |
|---|---|
33004 | The from number is not assigned to your account |
33009 | The from number is not in an active state |
33016 | Message body contains unsupported characters |
33019 | Message violates compliance rules |
33023 | Insufficient credits |
See Error Codes for the full reference.