Skip to content
Messages

Send a WhatsApp message or mark inbound as read

POST
/{phone_number_id}/messages
const url = 'https://api.kirimdev.com/v1/106540352242922/messages';
const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: '{"messaging_product":"whatsapp","recipient_type":"individual","to":"628123456789","recipient":"US.13491208655302741918","context":{"message_id":"wamid.HBgNNjI4MTIzNDU2Nzg5FQIAEhggMUE5MDM1QzVDMUYxQTAyRkVCNzZDQjVDMjFFRDQzNTUA"},"type":"text","text":{"body":"example","preview_url":true}}'
};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}

Meta-style messages endpoint. Two operations share the same path, distinguished by the request body:

Outbound send — body has a type field (text, image, document, video, audio, template, interactive). Body shape mirrors the Meta WhatsApp Cloud API; media variants accept either link (hosted URL) or id (Meta uploaded media). Optional top-level context: { message_id } creates a contextual reply — pass the inbound wamid from your webhook (messages[0].id), not a Kirim msg_* id. The sender is the WhatsApp account identified by phone_number_id in the URL path; there is no from field in the body. Pass Idempotency-Key to safely retry on network failure.

Read receipt — body has status: "read" + message_id (the inbound wamid from your webhook). Sends a read receipt back to the sender so the double-tick turns blue on their phone. Optional typing_indicator: { type: "text" } shows a typing bubble alongside the receipt; Meta auto-dismisses after ~25 seconds or on first outbound message.

Pre-send validation — the API performs compliance checks before queuing the send. A 422 response with error.code set to marketing_opted_out indicates the recipient has stopped marketing messages from your business (the customer did this via the WhatsApp app menu — webhook user_preferences notifies you of the change). account_restricted indicates Meta has restricted your WhatsApp account; check the account status via the dashboard. consent_required indicates the recipient has not opted in. customer_suspended indicates the operator has suspended (or archived) the customer that owns this WhatsApp account; sends are blocked until the customer is unsuspended via the dashboard. upstream_error indicates the WhatsApp account is disconnected. None of these are retryable until the underlying condition changes.

phone_number_id
required

Meta WhatsApp Business phone_number_id of the connected account that should send / own this resource. Discoverable via GET /v1/accounts (returned as phone_number_id on each row).

string
/^\d{6,20}$/
Example
106540352242922

Meta WhatsApp Business phone_number_id of the connected account that should send / own this resource. Discoverable via GET /v1/accounts (returned as phone_number_id on each row).

idempotency-key

Optional idempotency token. See /docs/idempotency.

string

Optional idempotency token. See /docs/idempotency.

Media type application/json
Any of:
Text
object
messaging_product
required
string
Allowed values: whatsapp
recipient_type
string
Allowed values: individual
to

Recipient’s WhatsApp phone number in E.164 (e.g. 628123456789). Either to or recipient is required; if both are supplied, Meta uses to (documented precedence).

string
Example
628123456789
recipient

Recipient’s Business-Scoped User ID (e.g. US.13491208655302741918) or parent BSUID. Alternative to to. Auth OTP templates (one-tap / zero-tap / copy-code) do NOT support BSUID recipients — Meta rejects with error 131062.

string
/^[A-Z]{2}\.(ENT\.)?[A-Za-z0-9]{1,128}$/
Example
US.13491208655302741918
context
Reply context
object
message_id
required

Meta WhatsApp message id (wamid) of the message to reply to. From the inbound webhook — NOT a Kirim msg_* external id.

string
>= 1 characters
Example
wamid.HBgNNjI4MTIzNDU2Nzg5FQIAEhggMUE5MDM1QzVDMUYxQTAyRkVCNzZDQjVDMjFFRDQzNTUA
type
required
string
Allowed values: text
text
required
object
body
required
string
>= 1 characters <= 4096 characters
preview_url
boolean

Message accepted for delivery, or read receipt sent.

Media type application/json
object
data
required
object
id
required
string
object
required
string
Allowed values: message
to

Recipient’s phone number in E.164 (with leading +). NULL when the message targeted a Business-Scoped User ID (BSUID) recipient and the contact has no known phone number. See docs/meta-docs/bsuid.md.

string | null
bsuid

Recipient contact’s Business-Scoped User ID. Present when the contact has been enriched with a BSUID from a Meta webhook (or from a BSUID-first send). NULL for phone-only contacts.

string | null
parent_bsuid

Parent BSUID for managed businesses with linked business portfolios. NULL for regular accounts.

string | null
username

Recipient contact’s WhatsApp username (case-insensitive comparison; case-preserving in storage). NULL when the contact has not adopted the WhatsApp username feature.

string | null
type
string
status
required
string
Allowed values: queued pending sent delivered read failed
created_at
string format: date-time
played_at

ISO-8601 timestamp when the recipient first played an outbound voice note. Populated from Meta’s played status webhook; omitted on non-voice messages and until playback occurs.

string format: date-time
conversation_id
string
message_id
string
error
object
code
required

Stable Kirim error code. Known values include outside_24h_window, marketing_opted_out, marketing_blocked_by_user, recipient_unavailable, unsupported_message_type, account_restricted, account_locked, consent_required, customer_suspended, permission_revoked, auth_expired, app_rate_limited, account_rate_limited, template_not_found, template_paused, template_policy_violation, flow_blocked, media_upload_failed, media_download_failed, undeliverable, policy_violation, upstream_error.

string
message
required
string
provider_code
required
number | null
request_id
required
string
Example
{
"data": {
"id": "msg_01HXYZABCDEFGHJKMNPQRSTVWX",
"object": "message",
"to": "+628123456789",
"bsuid": "US.13491208655302741918",
"parent_bsuid": "US.ENT.11815799212886844830",
"username": "pablomorales",
"status": "queued"
}
}

Validation failure

Media type application/json
object
error
required
object
type
required
string
Allowed values: invalid_request_error authentication_error permission_error not_found conflict rate_limit_error api_error
code
required
string
message
required
string
param
string
request_id
required
string
Example
{
"error": {
"type": "invalid_request_error",
"code": "invalid_phone_number",
"request_id": "req_01HXYZABCDEFGHJKMNPQRSTVWX"
}
}

Authentication failure

Media type application/json
object
error
required
object
type
required
string
Allowed values: invalid_request_error authentication_error permission_error not_found conflict rate_limit_error api_error
code
required
string
message
required
string
param
string
request_id
required
string
Example
{
"error": {
"type": "invalid_request_error",
"code": "invalid_phone_number",
"request_id": "req_01HXYZABCDEFGHJKMNPQRSTVWX"
}
}

Resource not found

Media type application/json
object
error
required
object
type
required
string
Allowed values: invalid_request_error authentication_error permission_error not_found conflict rate_limit_error api_error
code
required
string
message
required
string
param
string
request_id
required
string
Example
{
"error": {
"type": "invalid_request_error",
"code": "invalid_phone_number",
"request_id": "req_01HXYZABCDEFGHJKMNPQRSTVWX"
}
}

Semantic failure (e.g. idempotency key reuse, pre-send compliance guard, media not found)

Media type application/json
object
error
required
object
type
required
string
Allowed values: invalid_request_error authentication_error permission_error not_found conflict rate_limit_error api_error
code
required
string
message
required
string
param
string
request_id
required
string
Example
{
"error": {
"type": "invalid_request_error",
"code": "invalid_phone_number",
"request_id": "req_01HXYZABCDEFGHJKMNPQRSTVWX"
}
}

Rate limit exceeded

Media type application/json
object
error
required
object
type
required
string
Allowed values: invalid_request_error authentication_error permission_error not_found conflict rate_limit_error api_error
code
required
string
message
required
string
param
string
request_id
required
string
Example
{
"error": {
"type": "invalid_request_error",
"code": "invalid_phone_number",
"request_id": "req_01HXYZABCDEFGHJKMNPQRSTVWX"
}
}

Upstream WhatsApp error

Media type application/json
object
error
required
object
type
required
string
Allowed values: invalid_request_error authentication_error permission_error not_found conflict rate_limit_error api_error
code
required
string
message
required
string
param
string
request_id
required
string
Example
{
"error": {
"type": "invalid_request_error",
"code": "invalid_phone_number",
"request_id": "req_01HXYZABCDEFGHJKMNPQRSTVWX"
}
}

Service unavailable

Media type application/json
object
error
required
object
type
required
string
Allowed values: invalid_request_error authentication_error permission_error not_found conflict rate_limit_error api_error
code
required
string
message
required
string
param
string
request_id
required
string
Example
{
"error": {
"type": "invalid_request_error",
"code": "invalid_phone_number",
"request_id": "req_01HXYZABCDEFGHJKMNPQRSTVWX"
}
}