Messages API
Send text, media and templates; read the delivery timeline. 8 ops: send_message, send_bulk_messages, get_message, list_messages, cancel_message, retry_message, mark_message_read, get_media_url.
Send text, media and templates; read the delivery timeline.
send_message
writeSend a WhatsApp message · POST /api/v1/send_message · scope messages:write
Sends one WhatsApp message from a connected number (or the sandbox): text, template, image/audio/video/document/sticker, location, contacts, interactive (buttons, lists, CTA URL, Flows) or a reaction, optionally as a reply. Safeguards run first, in order: account and number eligibility, opt-out and consent, the 24-hour window (outside it only approved templates), template approval, rate limits (the send waits instead of failing), the plan's allowance and budget cap, and duplicate detection; a refusal says which (`safeguard`) and whose call it is (`controlled_by`). Returns the message: status submitted once Meta accepted it, then sent → delivered → read arrive by webhook (get_message shows the timeline). Use idempotency_key to make retries safe.
curl -X POST https://getwabi.dev/api/v1/send_message \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"type":"text","phone_number_id":"phone_number_id","to":"15550100001"}'npx @meterapp/wabi send_message --json '{"type":"text","phone_number_id":"phone_number_id","to":"15550100001"}'{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"text",
"template",
"image",
"audio",
"video",
"document",
"sticker",
"location",
"contacts",
"interactive",
"reaction"
],
"description": "The kind of message. Free-form types (everything but template) need the customer's 24-hour window to be open."
},
"text": {
"description": "type text: { body }.",
"type": "object",
"properties": {
"body": {
"type": "string",
"minLength": 1,
"maxLength": 4096
},
"preview_url": {
"type": "boolean"
}
},
"required": [
"body"
]
},
"template": {
"description": "type template: an approved template by name and language, with its variables.",
"type": "object",
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z0-9_]{1,512}$"
},
"language": {
"anyOf": [
{
"type": "string",
"minLength": 2,
"maxLength": 15
},
{
"type": "object",
"properties": {
"code": {
"type": "string",
"minLength": 2,
"maxLength": 15
}
},
"required": [
"code"
]
}
],
"description": "Template language code, e.g. en_US."
},
"components": {
"description": "Variable values, as Meta's components array: [{ type: 'body', parameters: [{ type: 'text', text: 'Ana' }] }].",
"maxItems": 20,
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"header",
"body",
"button",
"carousel"
]
},
"sub_type": {
"type": "string",
"enum": [
"quick_reply",
"url",
"copy_code",
"flow",
"catalog"
]
},
"index": {
"anyOf": [
{
"type": "integer",
"minimum": 0,
"maximum": 9
},
{
"type": "string",
"pattern": "^\\d$"
}
]
},
"parameters": {
"maxItems": 100,
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"text",
"currency",
"date_time",
"image",
"video",
"document",
"location",
"payload",
"coupon_code"
]
},
"text": {
"type": "string",
"maxLength": 4096
},
"parameter_name": {
"type": "string",
"maxLength": 100
}
},
"required": [
"type"
],
"additionalProperties": {}
}
}
},
"required": [
"type"
],
"additionalProperties": {}
}
}
},
"required": [
"name",
"language"
]
},
"image": {
"type": "object",
"properties": {
"id": {
"description": "A media id from Meta (an upload, or an inbound message's media id).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"link": {
"description": "A public https URL Meta downloads the file from.",
"type": "string",
"format": "starts_with",
"pattern": "^https:\\/\\/.*"
},
"caption": {
"type": "string",
"maxLength": 1024
},
"filename": {
"description": "Documents only: the file name the customer sees.",
"type": "string",
"maxLength": 240
}
}
},
"audio": {
"type": "object",
"properties": {
"id": {
"description": "A media id from Meta (an upload, or an inbound message's media id).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"link": {
"description": "A public https URL Meta downloads the file from.",
"type": "string",
"format": "starts_with",
"pattern": "^https:\\/\\/.*"
},
"caption": {
"type": "string",
"maxLength": 1024
},
"filename": {
"description": "Documents only: the file name the customer sees.",
"type": "string",
"maxLength": 240
}
}
},
"video": {
"type": "object",
"properties": {
"id": {
"description": "A media id from Meta (an upload, or an inbound message's media id).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"link": {
"description": "A public https URL Meta downloads the file from.",
"type": "string",
"format": "starts_with",
"pattern": "^https:\\/\\/.*"
},
"caption": {
"type": "string",
"maxLength": 1024
},
"filename": {
"description": "Documents only: the file name the customer sees.",
"type": "string",
"maxLength": 240
}
}
},
"document": {
"type": "object",
"properties": {
"id": {
"description": "A media id from Meta (an upload, or an inbound message's media id).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"link": {
"description": "A public https URL Meta downloads the file from.",
"type": "string",
"format": "starts_with",
"pattern": "^https:\\/\\/.*"
},
"caption": {
"type": "string",
"maxLength": 1024
},
"filename": {
"description": "Documents only: the file name the customer sees.",
"type": "string",
"maxLength": 240
}
}
},
"sticker": {
"type": "object",
"properties": {
"id": {
"description": "A media id from Meta (an upload, or an inbound message's media id).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"link": {
"description": "A public https URL Meta downloads the file from.",
"type": "string",
"format": "starts_with",
"pattern": "^https:\\/\\/.*"
},
"caption": {
"type": "string",
"maxLength": 1024
},
"filename": {
"description": "Documents only: the file name the customer sees.",
"type": "string",
"maxLength": 240
}
}
},
"location": {
"description": "type location: a pin with an optional name and address.",
"type": "object",
"properties": {
"latitude": {
"type": "number",
"minimum": -90,
"maximum": 90
},
"longitude": {
"type": "number",
"minimum": -180,
"maximum": 180
},
"name": {
"type": "string",
"maxLength": 1000
},
"address": {
"type": "string",
"maxLength": 1000
}
},
"required": [
"latitude",
"longitude"
]
},
"contacts": {
"description": "type contacts: contact cards in Meta's format.",
"minItems": 1,
"maxItems": 20,
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
}
},
"interactive": {
"description": "type interactive: reply buttons, a list, a CTA URL, a Flow or a location request, in Meta's interactive format.",
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"button",
"list",
"cta_url",
"flow",
"location_request_message",
"product",
"product_list",
"catalog_message"
]
}
},
"required": [
"type"
],
"additionalProperties": {}
},
"reaction": {
"description": "type reaction: an emoji on a message (our message id or the wamid); empty emoji removes it.",
"type": "object",
"properties": {
"message_id": {
"type": "string",
"minLength": 1
},
"emoji": {
"type": "string",
"maxLength": 16
}
},
"required": [
"message_id",
"emoji"
]
},
"phone_number_id": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "The number to send from: its id from list_whatsapp_accounts or get_sandbox (phone_number.id), or Meta's phone_number_id."
},
"reply_to": {
"description": "A message id (ours or the wamid) to quote as the reply's context.",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"scheduled_at": {
"description": "Send at this time (ISO 8601) instead of now; at most 30 days ahead. cancel_message cancels it until then.",
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
"allow_duplicate": {
"default": false,
"description": "Send even if the same content went to this customer from this number within the last minute.",
"type": "boolean"
},
"campaign_id": {
"description": "The campaign this send belongs to (campaigns.id); the message is then attributed to it (sender_kind campaign).",
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"to": {
"type": "string",
"minLength": 6,
"maxLength": 32,
"description": "The customer's WhatsApp number in international format (e.g. 15550100001 or +44 7700 900123)."
},
"idempotency_key": {
"description": "Retry-safe key: a second call with the same key returns the first message instead of sending again.",
"type": "string",
"minLength": 1,
"maxLength": 200
}
},
"required": [
"type",
"phone_number_id",
"to"
]
}send_bulk_messages
writeSend to many recipients · POST /api/v1/send_bulk_messages · scope messages:write
Queues the same message (usually an approved template, with per-recipient variables) to up to 500 customers from one number, now or at scheduled_at. Every recipient passes the same safeguards as send_message; refused ones are listed with the reason and nothing is stored for them. Accepted messages go out through the queue at the number's rate limit. Live numbers need a plan with bulk sends (Growth and up); the sandbox is free. Returns the queued message ids and the refusals.
curl -X POST https://getwabi.dev/api/v1/send_bulk_messages \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"type":"text","phone_number_id":"phone_number_id","recipients":[{"to":"15550100001"}]}'npx @meterapp/wabi send_bulk_messages --json '{"type":"text","phone_number_id":"phone_number_id","recipients":[{"to":"15550100001"}]}'{
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"text",
"template",
"image",
"audio",
"video",
"document",
"sticker",
"location",
"contacts",
"interactive",
"reaction"
],
"description": "The kind of message. Free-form types (everything but template) need the customer's 24-hour window to be open."
},
"text": {
"description": "type text: { body }.",
"type": "object",
"properties": {
"body": {
"type": "string",
"minLength": 1,
"maxLength": 4096
},
"preview_url": {
"type": "boolean"
}
},
"required": [
"body"
]
},
"template": {
"description": "type template: an approved template by name and language, with its variables.",
"type": "object",
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z0-9_]{1,512}$"
},
"language": {
"anyOf": [
{
"type": "string",
"minLength": 2,
"maxLength": 15
},
{
"type": "object",
"properties": {
"code": {
"type": "string",
"minLength": 2,
"maxLength": 15
}
},
"required": [
"code"
]
}
],
"description": "Template language code, e.g. en_US."
},
"components": {
"description": "Variable values, as Meta's components array: [{ type: 'body', parameters: [{ type: 'text', text: 'Ana' }] }].",
"maxItems": 20,
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"header",
"body",
"button",
"carousel"
]
},
"sub_type": {
"type": "string",
"enum": [
"quick_reply",
"url",
"copy_code",
"flow",
"catalog"
]
},
"index": {
"anyOf": [
{
"type": "integer",
"minimum": 0,
"maximum": 9
},
{
"type": "string",
"pattern": "^\\d$"
}
]
},
"parameters": {
"maxItems": 100,
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"text",
"currency",
"date_time",
"image",
"video",
"document",
"location",
"payload",
"coupon_code"
]
},
"text": {
"type": "string",
"maxLength": 4096
},
"parameter_name": {
"type": "string",
"maxLength": 100
}
},
"required": [
"type"
],
"additionalProperties": {}
}
}
},
"required": [
"type"
],
"additionalProperties": {}
}
}
},
"required": [
"name",
"language"
]
},
"image": {
"type": "object",
"properties": {
"id": {
"description": "A media id from Meta (an upload, or an inbound message's media id).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"link": {
"description": "A public https URL Meta downloads the file from.",
"type": "string",
"format": "starts_with",
"pattern": "^https:\\/\\/.*"
},
"caption": {
"type": "string",
"maxLength": 1024
},
"filename": {
"description": "Documents only: the file name the customer sees.",
"type": "string",
"maxLength": 240
}
}
},
"audio": {
"type": "object",
"properties": {
"id": {
"description": "A media id from Meta (an upload, or an inbound message's media id).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"link": {
"description": "A public https URL Meta downloads the file from.",
"type": "string",
"format": "starts_with",
"pattern": "^https:\\/\\/.*"
},
"caption": {
"type": "string",
"maxLength": 1024
},
"filename": {
"description": "Documents only: the file name the customer sees.",
"type": "string",
"maxLength": 240
}
}
},
"video": {
"type": "object",
"properties": {
"id": {
"description": "A media id from Meta (an upload, or an inbound message's media id).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"link": {
"description": "A public https URL Meta downloads the file from.",
"type": "string",
"format": "starts_with",
"pattern": "^https:\\/\\/.*"
},
"caption": {
"type": "string",
"maxLength": 1024
},
"filename": {
"description": "Documents only: the file name the customer sees.",
"type": "string",
"maxLength": 240
}
}
},
"document": {
"type": "object",
"properties": {
"id": {
"description": "A media id from Meta (an upload, or an inbound message's media id).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"link": {
"description": "A public https URL Meta downloads the file from.",
"type": "string",
"format": "starts_with",
"pattern": "^https:\\/\\/.*"
},
"caption": {
"type": "string",
"maxLength": 1024
},
"filename": {
"description": "Documents only: the file name the customer sees.",
"type": "string",
"maxLength": 240
}
}
},
"sticker": {
"type": "object",
"properties": {
"id": {
"description": "A media id from Meta (an upload, or an inbound message's media id).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"link": {
"description": "A public https URL Meta downloads the file from.",
"type": "string",
"format": "starts_with",
"pattern": "^https:\\/\\/.*"
},
"caption": {
"type": "string",
"maxLength": 1024
},
"filename": {
"description": "Documents only: the file name the customer sees.",
"type": "string",
"maxLength": 240
}
}
},
"location": {
"description": "type location: a pin with an optional name and address.",
"type": "object",
"properties": {
"latitude": {
"type": "number",
"minimum": -90,
"maximum": 90
},
"longitude": {
"type": "number",
"minimum": -180,
"maximum": 180
},
"name": {
"type": "string",
"maxLength": 1000
},
"address": {
"type": "string",
"maxLength": 1000
}
},
"required": [
"latitude",
"longitude"
]
},
"contacts": {
"description": "type contacts: contact cards in Meta's format.",
"minItems": 1,
"maxItems": 20,
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
}
},
"interactive": {
"description": "type interactive: reply buttons, a list, a CTA URL, a Flow or a location request, in Meta's interactive format.",
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"button",
"list",
"cta_url",
"flow",
"location_request_message",
"product",
"product_list",
"catalog_message"
]
}
},
"required": [
"type"
],
"additionalProperties": {}
},
"reaction": {
"description": "type reaction: an emoji on a message (our message id or the wamid); empty emoji removes it.",
"type": "object",
"properties": {
"message_id": {
"type": "string",
"minLength": 1
},
"emoji": {
"type": "string",
"maxLength": 16
}
},
"required": [
"message_id",
"emoji"
]
},
"phone_number_id": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "The number to send from: its id from list_whatsapp_accounts or get_sandbox (phone_number.id), or Meta's phone_number_id."
},
"reply_to": {
"description": "A message id (ours or the wamid) to quote as the reply's context.",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"scheduled_at": {
"description": "Send at this time (ISO 8601) instead of now; at most 30 days ahead. cancel_message cancels it until then.",
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
"allow_duplicate": {
"default": false,
"description": "Send even if the same content went to this customer from this number within the last minute.",
"type": "boolean"
},
"campaign_id": {
"description": "The campaign this send belongs to (campaigns.id); the message is then attributed to it (sender_kind campaign).",
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"recipients": {
"minItems": 1,
"maxItems": 500,
"type": "array",
"items": {
"type": "object",
"properties": {
"to": {
"type": "string",
"minLength": 6,
"maxLength": 32
},
"template_components": {
"description": "This recipient's template variables (replaces the shared ones).",
"maxItems": 20,
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"header",
"body",
"button",
"carousel"
]
},
"sub_type": {
"type": "string",
"enum": [
"quick_reply",
"url",
"copy_code",
"flow",
"catalog"
]
},
"index": {
"anyOf": [
{
"type": "integer",
"minimum": 0,
"maximum": 9
},
{
"type": "string",
"pattern": "^\\d$"
}
]
},
"parameters": {
"maxItems": 100,
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"text",
"currency",
"date_time",
"image",
"video",
"document",
"location",
"payload",
"coupon_code"
]
},
"text": {
"type": "string",
"maxLength": 4096
},
"parameter_name": {
"type": "string",
"maxLength": 100
}
},
"required": [
"type"
],
"additionalProperties": {}
}
}
},
"required": [
"type"
],
"additionalProperties": {}
}
},
"idempotency_key": {
"type": "string",
"minLength": 1,
"maxLength": 200
}
},
"required": [
"to"
]
}
}
},
"required": [
"type",
"phone_number_id",
"recipients"
]
}get_message
readGet a message · POST /api/v1/get_message · scope messages:read or GET …?input=
One message with its full status timeline (queued, submitted, sent, delivered, read, or failed with Meta's error code and whose problem it is), Meta's pricing category, attempts and retries.
curl -X POST https://getwabi.dev/api/v1/get_message \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'npx @meterapp/wabi get_message --json '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'{
"type": "object",
"properties": {
"message_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
"required": [
"message_id"
]
}list_messages
readList messages · POST /api/v1/list_messages · scope messages:read or GET …?input=
Messages, newest first, filtered by conversation, contact, number, direction or status; dead_letter: true lists sends that exhausted their retries (retry_message re-queues them).
curl -X POST https://getwabi.dev/api/v1/list_messages \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi list_messages{
"type": "object",
"properties": {
"conversation_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Conversation id."
},
"contact_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Contact id."
},
"phone_number_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Connected sender id (whatsapp_phone_numbers.id, not Meta's phone_number_id)."
},
"direction": {
"type": "string",
"enum": [
"inbound",
"outbound"
]
},
"status": {
"type": "string",
"enum": [
"queued",
"submitted",
"sent",
"delivered",
"read",
"failed",
"rejected",
"rate_limited",
"held",
"canceled",
"expired",
"received"
]
},
"dead_letter": {
"type": "boolean"
},
"limit": {
"default": 50,
"description": "Page size, at most 200.",
"type": "integer",
"minimum": 1,
"maximum": 200
},
"offset": {
"default": 0,
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
}
}cancel_message
writeCancel a message · POST /api/v1/cancel_message · scope messages:write
Cancels an outbound message that has not gone to WhatsApp yet (scheduled, queued, rate limited or held). A message Meta already accepted cannot be recalled.
curl -X POST https://getwabi.dev/api/v1/cancel_message \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'npx @meterapp/wabi cancel_message --json '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'{
"type": "object",
"properties": {
"message_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
"required": [
"message_id"
]
}retry_message
writeRetry a message · POST /api/v1/retry_message · scope messages:write
Re-queues an outbound message that never reached WhatsApp (dead-lettered after its retries, refused by Meta, or expired) for a fresh set of attempts, through the same safeguards at send time. Fix the cause first (a template, a token, the window).
curl -X POST https://getwabi.dev/api/v1/retry_message \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'npx @meterapp/wabi retry_message --json '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'{
"type": "object",
"properties": {
"message_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
"required": [
"message_id"
]
}mark_message_read
writeMark a message read · POST /api/v1/mark_message_read · scope messages:write
Marks an inbound message as read on the customer's phone (blue ticks), optionally showing a typing indicator while a reply is written (WhatsApp shows it up to 25 seconds or until you reply).
curl -X POST https://getwabi.dev/api/v1/mark_message_read \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'npx @meterapp/wabi mark_message_read --json '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'{
"type": "object",
"properties": {
"message_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"typing_indicator": {
"default": false,
"type": "boolean"
}
},
"required": [
"message_id"
]
}get_media_url
readGet a media link · POST /api/v1/get_media_url · scope messages:read or GET …?input=
A link (valid 10 minutes) to the image, voice note, video, document or sticker a customer sent; the file is kept in our storage, so it outlives Meta's own short-lived link. Returns the URL, MIME type and size.
curl -X POST https://getwabi.dev/api/v1/get_media_url \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'npx @meterapp/wabi get_media_url --json '{"message_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'{
"type": "object",
"properties": {
"message_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
"required": [
"message_id"
]
}