WhatsApp APIAPI

WhatsApp: send messages through the API

Send text, image, video, audio, document, sticker, location, buttons and lists on WhatsApp through the XBASE API, within the 24-hour window.

Updated on

POST /whatsapp/messages sends a free-form message to a contact, within the 24-hour window. Outside it, send a template.

Send a message

POST https://api.xbase.app/api/external/v1/whatsapp/messages
FieldTypeRequiredRule
numberIdtextyesSending number (list the numbers)
totextyesContact phone with the country code, 8 to 15 digits. +, spaces, hyphens and parentheses are removed
typetextyestext, image, video, audio, document, sticker, location, buttons, list or flow
type objectobjectyesA field with the same name as type, like "text": { "body": "…" }. See each type below
curl -X POST https://api.xbase.app/api/external/v1/whatsapp/messages \
  -H "Authorization: Bearer $XBASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
    "to": "15555550100",
    "type": "text",
    "text": { "body": "Your order is out for delivery and arrives today by 6 PM." }
  }'

The response is in Response. An extra field in the body is rejected with 400.

Text

{
  "numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
  "to": "15555550100",
  "type": "text",
  "text": { "body": "Hi, Mary! How can I help?" }
}

text.body has 1 to 4,096 characters. Links in the text are clickable on WhatsApp.

Media

Image, video, audio, document and sticker go by public https URL: Meta downloads the file from it. An http URL, a URL with a username and password, or an internal or private network address returns 400.

{
  "numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
  "to": "15555550100",
  "type": "image",
  "image": {
    "link": "https://cdn.mycompany.com/catalog/blue-sneakers.jpg",
    "caption": "Blue sneakers, sizes 6 to 12"
  }
}
typeObjectMeta rules
imagelink, captionJPEG or PNG, up to 5 MB
videolink, captionMP4 or 3GP, up to 16 MB
audiolink, voiceAAC, MP3, MP4 or OGG, up to 16 MB. "voice": true shows it as a voice note, and the file must be OGG with the Opus codec
documentlink, filename, captionAny file, up to 100 MB. filename (up to 240 characters) is the name the contact sees
stickerlinkWebP of 512 × 512 pixels

caption is optional, up to 1,024 characters.

Location

{
  "numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
  "to": "15555550100",
  "type": "location",
  "location": {
    "latitude": -23.5614,
    "longitude": -46.6559,
    "name": "Paulista store",
    "address": "Av. Paulista, 1000, São Paulo"
  }
}
FieldRule
location.latitudeFrom −90 to 90
location.longitudeFrom −180 to 180
location.name · location.addressOptional, up to 1,000 characters

Buttons

{
  "numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
  "to": "15555550100",
  "type": "buttons",
  "buttons": {
    "header": "Order 12345",
    "body": "Can we confirm the delivery for tomorrow?",
    "footer": "Tap an option",
    "buttons": [
      { "id": "confirm", "title": "Confirm" },
      { "id": "reschedule", "title": "Reschedule" }
    ]
  }
}
FieldRule
buttons.bodyMessage text, up to 1,024 characters
buttons.header · buttons.footerOptional, up to 60 characters
buttons.buttons1 to 3 buttons. id (up to 256 characters, no repeats) comes back when the contact taps; title has up to 20 characters

List

{
  "numberId": "6d1f3c2a-4b5e-4f60-9a7b-8c9d0e1f2a3b",
  "to": "15555550100",
  "type": "list",
  "list": {
    "body": "Choose the delivery time.",
    "button": "See times",
    "rows": [
      { "id": "morning", "title": "Morning", "description": "8 AM to 12 PM" },
      { "id": "afternoon", "title": "Afternoon", "description": "1 PM to 6 PM" }
    ]
  }
}
FieldRule
list.bodyMessage text, up to 1,024 characters
list.buttonText of the button that opens the list, up to 20 characters
list.rows1 to 10 rows. id (up to 200 characters, no repeats) comes back when the contact picks it; title up to 24 characters; description optional, up to 72
list.header · list.footerOptional, up to 60 characters

The tapped button or the chosen row arrives as whatsapp.message.received in the workspace webhooks, with the id you sent.

Flow

To open a WhatsApp Flow (a form inside WhatsApp), use "type": "flow". The fields and an example are in Flows.

Response

Response 200:

{
  "status": "accepted",
  "messageId": "wamid.HBgNNTUyMTk5OTk5MDAwMBUCABEYEjQ1QzM1RkQ2RDhGNjZBMDQ3MwA=",
  "threadId": "c1d7f0b2-5a4e-4f3d-9c2b-1a0e9d8c7b6a",
  "dialogId": "5a0e4c31-7b2d-4e8f-a1c3-9d6b5e4f3a21"
}
FieldDescription
statusaccepted: Meta accepted the send. Delivery and read come later, in the webhook events
messageIdMessage id at Meta (wamid.…). It goes in the reaction and shows up in the events
threadIdThe contact's conversation at XBASE
dialogIdMessage in the conversation

Errors

StatuscodeWhen
400(no code)Invalid field: phone, unknown type, empty or too long text, http or internal URL, extra field. The message says which one
404NUMBER_NOT_FOUNDThe number does not exist in this workspace
422NUMBER_DISCONNECTEDThe number is disconnected
422WINDOW_CLOSEDOutside the 24-hour window. Send a template
422SEND_REJECTEDXBASE refused before sending. The message says why
422META_ERRORMeta refused, for example, media it could not download. details.metaCode has Meta's code

In WINDOW_CLOSED, details.reason says why the window is closed: no_contact_message (the contact never wrote to this number), window_expired (their last message is more than 24 hours old) or awaiting_template_reply (the last send was a template and the contact has not replied yet). details.lastContactMessageAt has the date of the contact's last message.

{
  "statusCode": 422,
  "message": "Janela de atendimento de 24h expirada. Envie apenas templates aprovados pelo WhatsApp.",
  "code": "WINDOW_CLOSED",
  "details": { "reason": "window_expired", "lastContactMessageAt": "2026-11-03T14:02:11.000Z" }
}

Frequently asked questions

See also

On this page