Messaging

Message status and history

Track queued messages until they are sent or failed.

Status lifecycle

Every send returns a `message_id`. Store that ID with your own order, user, ticket, or notification record.

The status endpoint returns sender, receiver, timestamps, attempt count, WhatsApp message ID when available, and the latest error message when delivery fails.

`wamid` is the WhatsApp-side message ID. It is usually null while a message is queued and can remain null when delivery fails before WhatsApp accepts the message.

Endpoint reference

Use the same API key used to send the message. A service key can also read status for admin workflows.

GET/api/messages/{message_id}

Return current delivery status and debug fields for a message.

ParameterTypeDescription
x-api-keyrequiredheaderAPI key that can access the message workspace.
message_idrequiredpath uuidMessage ID returned by a send endpoint.
200 response
json
{
  "message_id": "2b7a0bd5-1a0d-4e6c-85b1-a7f44f92dfb0",
  "workspace_id": "f2aa1d49-9f4b-4f37-9918-d8d8f13fb530",
  "sender_id": "9f4b5d4c-0000-4000-9000-123456789abc",
  "api_client_id": "38b7bbd3-0631-4ea1-a93b-09d560a645d3",
  "receiver_number": "212612345678",
  "message_text": "Your appointment is confirmed.",
  "status": "sent",
  "wamid": "wamid.HBgM...",
  "source_system": "appointments",
  "source_reference": "booking_1042",
  "error_message": null,
  "attempts": 1,
  "queued_at": "2026-06-29T16:12:01.000Z",
  "sent_at": "2026-06-29T16:12:04.000Z",
  "failed_at": null,
  "created_at": "2026-06-29T16:12:01.000Z"
}

Polling workflow

Use polling when webhooks are not configured or when the user is actively waiting for a result. For background reconciliation, prefer webhooks.

Do not poll forever. Store the last known status and move old queued messages into an operator review state if they do not change after your business timeout.

poll-status.ts
ts
async function waitForMessage(messageId: string) {
  for (let attempt = 0; attempt < 12; attempt += 1) {
    const response = await fetch(`${process.env.HOOKMESSAGE_BASE_URL}/api/messages/${messageId}`, {
      headers: { "x-api-key": process.env.HOOKMESSAGE_API_KEY! },
    });
    const status = await response.json();

    if (status.status === "sent" || status.status === "failed") {
      return status;
    }

    await new Promise((resolve) => setTimeout(resolve, attempt < 3 ? 5000 : 15000));
  }

  return { status: "review_required", message_id: messageId };
}

Need help?

Use Book Integration Help if you want setup help for OTP or automated customer messaging.

Next steps