Skip to content
PaalChat Docs

message.status

.md
Event POST <your callback URL>

An outgoing message moved to sent, delivered, read, failed or unknown. Carries your reference. Statuses only move forward - ignore updates that would move one back. unknown means Meta's answer to the send was lost (timeout or server error): it may or may not have reached the customer, and it is never sent again automatically. If Meta did send it, a later sent/delivered/read arrives for the same message. pricing is what Meta reports it charges for the message; Meta bills the business's WhatsApp Business Account directly, never PaalChat.

Verify every callback

Check X-PaalChat-Signature on the raw body, reject timestamps older than 5 minutes, and de-duplicate on X-PaalChat-Delivery. Answer 2xx within 10 seconds. See Webhooks and callbacks.

Headers

X-PaalChat-Event string
X-PaalChat-Delivery string

Unique per event (equals body id). De-duplicate on it.

Format uuid.

X-PaalChat-Signature string

t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>. Reject if invalid or older than 300 seconds.

Pattern ^t=\d+,v1=[a-f0-9]{64}$.

Body

id string

Equals X-PaalChat-Delivery.

Format uuid.

sandbox boolean

true for sandbox businesses (test keys) - nothing reached WhatsApp.

event any

Always message.status.

occurred_at string

Format date-time.

business object
external_id string business.external_id
data object
message_id integer data.message_id
channel string data.channel

One of: whatsapp, sms, email.

reference string | null data.reference
wamid string | null data.wamid

The provider's message id (Meta's wamid; Arkesel's, Hubtel's or Resend's id for SMS and email).

contact_id integer | null data.contact_id
conversation_id integer | null data.conversation_id
to string data.to
phone_number_id string | null data.phone_number_id
status string data.status

One of: sent, delivered, read, failed, unknown.

previous_status string data.previous_status

Outgoing moves forward only (queued < sent < delivered < read); failed is final and never follows read. cancelled: a scheduled message withdrawn before it went. unknown follows queued when Meta's answer to the send was lost - it may still move to sent, delivered, read or failed if Meta reports the message.

One of: queued, unknown, sent, delivered, read, failed, received, cancelled.

error object | null data.error
code string data.error.code

Meta's error code (e.g. 131026) or a platform code (connection_unavailable, no_message_id, circuit_open when Meta was failing for longer than the send's retries; for unknown: http_5xx or no_response).

message string data.error.message
pricing object | null data.pricing

Meta's pricing for the message, from its status webhooks (null until Meta reports it, and for incoming messages). Meta bills the business's WhatsApp Business Account directly; PaalChat never charges for Meta messages. From 2026-10-01 replies inside the 24-hour window are billable too.

billable boolean | null data.pricing.billable

Whether Meta charges for this message.

category string | null data.pricing.category

Meta's pricing category.

Example marketing.

type string | null data.pricing.type

regular (charged), free_customer_service or free_entry_point.

Example regular.

model string | null data.pricing.model

Meta's pricing model: PMP (per message).

Example PMP.

sent_at string | null data.sent_at

Format date-time.

delivered_at string | null data.delivered_at

Format date-time.

read_at string | null data.read_at

Format date-time.

failed_at string | null data.failed_at

Format date-time.