Skip to content

Delivery reports

A 200 from the send endpoint means queued, not delivered. Delivery happens a moment later, and the message history is where you follow it.

GEThttps://api.9bits.net/whatsapp/ng/v1/whatsapp/report/messages

Terminal window
curl -G https://api.9bits.net/whatsapp/ng/v1/whatsapp/report/messages \
-H "Authorization: Bearer $NINEBITS_WA_KEY" \
-d start_date=2026-08-01 \
-d end_date=2026-08-31 \
-d per_page=25
{
"data": [
{
"message_id": "2d665262-ffca-4289-a6cb-4126910d868b",
"provider_message_id": "wamid.HBgNMjM0ODAyMjIyODUwMhUCABEYEjQ1MjI1REMy",
"sender": "2349060008502",
"recipient": "2348022228502",
"message_type": "template",
"template_name": "moviezone",
"language": "en",
"category": "marketing",
"content": "Hi Ada, take 25% off everything until the timer runs out.",
"status": "read",
"direction": "MT",
"cost": 85,
"currency": "ngn",
"created_at": "2026-08-11T20:35:54+01:00",
"sent_at": "2026-08-11T20:35:56+01:00",
"delivered_at": "2026-08-11T20:35:56+01:00",
"read_at": "2026-08-11T20:36:00+01:00"
}
],
"page": 1,
"per_page": 25,
"total": 126
}

Match on message_id — the value the send returned.

StatusMeaning
queuedAccepted and billed by us; not yet handed to WhatsApp.
submittedHanded to WhatsApp, awaiting their acknowledgement.
sentWhatsApp accepted it. A wamid now exists.
deliveredIt reached the handset.
readThe recipient opened it.
failedIt will not be delivered.

Each timestamp field is populated as the message passes that stage, so delivered_at being null on a sent message simply means it hasn’t arrived yet.

FieldWhoseUse it for
message_idOursEverything on our side — looking a message up here, matching it to what your send call returned.
provider_message_idWhatsApp’s wamid.…Matching against Meta’s own tooling and reports.
ParameterNotes
start_date / end_dateYYYY-MM-DD.
statusOne of the values above. status=failed is the useful one.
page / per_pageDefaults 1 and 25. total is the unpaginated count.

Poll for failures across a run:

Terminal window
curl -G https://api.9bits.net/whatsapp/ng/v1/whatsapp/report/messages \
-H "Authorization: Bearer $NINEBITS_WA_KEY" \
-d start_date=2026-08-11 \
-d end_date=2026-08-11 \
-d status=failed

direction distinguishes them: MT is a message you sent, MO one a customer sent you.

Inbound messages matter beyond their content — a customer message is what opens the 24-hour session window that lets you reply with free-form text. The created_at of the most recent MO message from a number is when their window started.

WhatsApp messages are billed on acceptance, with no refunds. Everything we can check, we check before charging you — a template that doesn’t exist, a wrong params count, a closed session window. Those cost nothing.

What we cannot know until Meta answers — a number that no longer has WhatsApp, a media URL that stopped resolving, a recipient who blocked you — is charged, and then shows up here as failed.

GEThttps://api.9bits.net/whatsapp/ng/v1/whatsapp/senders

[
{
"id": 6,
"display_name": "9bits Technologies (Live)",
"phone_number": "2349060008502",
"status": "active",
"is_default": true,
"quality_rating": "GREEN",
"messaging_tier": "TIER_1K"
}
]

Pass a sender’s id as wa_account_id on a send; omit it and the one with is_default: true is used.