Messaging
Sending messages
Every message is one POST /v1/accounts/{AC}/messages. The type field says what kind of message it is, and a block of the same name carries its content.
The request
| Field | Meaning |
|---|---|
from | Your sender’s SID (WA…). Or give senderPoolSid to let ShipMsg pick one from a pool. |
to | The recipient in international format, +2250700000000. National numbers work with defaultCountry. Or address a saved contact with contactSid. |
type | One of the types below. |
metadata | Up to 16 of your own string values (order IDs, user IDs), 512 characters each. Echoed on every event for this message. |
sendAt | Schedule for later (ISO 8601). |
validityPeriodSeconds | Give up if the message cannot be sent within this time; it then ends as expired. |
statusCallbackUrl | Also send this message’s statuses to a verified endpoint of yours. |
The answer is 202 Accepted with the message in status queued. Its progress — sent, delivered, read, or failed with a reason — arrives as events.
A complete template send in each language:
curl -X POST "https://api.shipmsg.com/v1/accounts/$SHIPMESSAGES_ACCOUNT_SID/messages" \
-H "Authorization: Bearer $SHIPMESSAGES_API_KEY" \
-H "ShipMessages-Version: 2026-08-21" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"from": "'"$SHIPMESSAGES_SANDBOX_SENDER_SID"'",
"to": "+15550000001",
"type": "template",
"template": {
"name": "order_ready",
"language": "en_US",
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "Ada"
},
{
"type": "text",
"text": "A-1042"
}
]
}
]
}
}'The 24-hour service window
WhatsApp only lets a business send free-form messages (text, media, interactive …) within 24 hours of the customer’s last message to it. Outside that window, send an approved template; anything else is refused with SERVICE_WINDOW_CLOSED. Inbound messages carry serviceWindowExpiresAt so you know how long you have.
Message types
Text
Up to 4,096 characters. Set previewUrl to show a link preview.
{
"from": "WA…",
"to": "+15550000001",
"type": "text",
"text": {
"body": "Your table for 2 is booked for 8 pm. Reply CHANGE to move it.",
"previewUrl": false
}
}Template
The only type you can send outside the 24-hour service window. Parameters fill the template’s {{1}}, {{2}} … in order (or by name for named templates).
{
"from": "WA…",
"to": "+15550000001",
"type": "template",
"template": {
"name": "order_ready",
"language": "en_US",
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "Ada"
},
{
"type": "text",
"text": "A-1042"
}
]
}
]
}
}Image, video, audio, document and sticker
Upload the file first (below), then send it by its ME… SID. The same media block serves every media type; caption is ignored for audio and stickers; voice: true sends audio as a voice note.
{
"from": "WA…",
"to": "+15550000001",
"type": "image",
"media": {
"sid": "ME…",
"caption": "Your receipt"
}
}Document with a file name
The file name is what the customer sees in WhatsApp.
{
"from": "WA…",
"to": "+15550000001",
"type": "document",
"media": {
"sid": "ME…",
"filename": "invoice-1042.pdf",
"caption": "Invoice 1042"
}
}Location
Latitude and longitude are required; name and address are shown under the pin.
{
"from": "WA…",
"to": "+15550000001",
"type": "location",
"location": {
"latitude": 5.3364,
"longitude": -4.0267,
"name": "Our shop",
"address": "Rue des Jardins, Abidjan"
}
}Contact card
Up to 10 cards; formattedName is required on each.
{
"from": "WA…",
"to": "+15550000001",
"type": "contacts",
"contacts": {
"contacts": [
{
"formattedName": "ShipMsg Support",
"phones": [
{
"value": "+15550000001",
"type": "WORK"
}
]
}
]
}
}Reaction
React to a message the customer sent, by its WhatsApp message ID. An empty emoji removes the reaction.
{
"from": "WA…",
"to": "+15550000001",
"type": "reaction",
"reaction": {
"targetMessageId": "wamid.…",
"emoji": "👍"
}
}Reply buttons
Up to 3 buttons of up to 20 characters. The customer’s choice arrives as an inbound message carrying the button id.
{
"from": "WA…",
"to": "+15550000001",
"type": "interactive",
"interactive": {
"subtype": "button",
"body": "Is 8 pm still good for you?",
"buttons": [
{
"id": "keep",
"title": "Yes, keep it"
},
{
"id": "move",
"title": "Change time"
}
]
}
}List
Up to 10 sections and 10 rows in total behind one button.
{
"from": "WA…",
"to": "+15550000001",
"type": "interactive",
"interactive": {
"subtype": "list",
"body": "Which day suits you?",
"button": "Choose a day",
"sections": [
{
"title": "This week",
"rows": [
{
"id": "thu",
"title": "Thursday"
},
{
"id": "fri",
"title": "Friday",
"description": "Busy after 7 pm"
}
]
}
]
}
}Link button
One button that opens a URL.
{
"from": "WA…",
"to": "+15550000001",
"type": "interactive",
"interactive": {
"subtype": "cta_url",
"body": "Track your parcel",
"displayText": "Open tracking",
"url": "https://example.com/track/1042"
}
}Location request
Asks the customer to share their location; the answer arrives as an inbound location message.
{
"from": "WA…",
"to": "+15550000001",
"type": "interactive",
"interactive": {
"subtype": "location_request",
"body": "Where should we deliver?"
}
}Flow
Opens a published WhatsApp Flow (a form inside WhatsApp).
{
"from": "WA…",
"to": "+15550000001",
"type": "flow",
"flow": {
"flowSid": "FL…",
"body": "Book your appointment",
"ctaText": "Book now",
"action": "navigate",
"screen": "WELCOME"
}
}Catalog, product and product list
Need a Meta commerce catalog connected to the WhatsApp Business Account. Types: catalog, product, product_list, carousel.
{
"from": "WA…",
"to": "+15550000001",
"type": "catalog",
"catalog": {
"catalogId": "1234567890",
"body": "Browse this week’s menu",
"thumbnailProductRetailerId": "sku-42"
}
}Uploading media
Upload a file as multipart form data (field file), or ask ShipMsg to fetch it from a public HTTPS URL. Every file is virus-scanned; send it once its state is READY (sending earlier answers MEDIA_NOT_READY, which is safe to retry).
curl -X POST "https://api.shipmsg.com/v1/accounts/$SHIPMESSAGES_ACCOUNT_SID/media" \
-H "Authorization: Bearer $SHIPMESSAGES_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F "file=@receipt.jpg"
# or
curl -X POST "https://api.shipmsg.com/v1/accounts/$SHIPMESSAGES_ACCOUNT_SID/media" \
-H "Authorization: Bearer $SHIPMESSAGES_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/receipt.jpg"}'Message statuses
| Status | Meaning |
|---|---|
queued | Accepted by ShipMsg, waiting to be sent (see queuedReason when it is held back on purpose). |
scheduled | Waiting for its sendAt time. |
sending | Being handed to WhatsApp. |
sent | WhatsApp accepted it. |
delivered | It reached the customer’s phone. |
read / played | The customer opened it (or played a voice note), if they share read receipts. |
failed | It will not be delivered; error says why. |
canceled / expired | You canceled it, or its validity period ran out before it could be sent. |
unknown | WhatsApp has not said what happened. ShipMsg never guesses “delivered”. |