> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useboom.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Message logs

> List what your organization sent and what happened to each message — delivered, failed, or blocked — over MCP or the API.

Every message Boom sends or receives leaves a row with its delivery status and,
when something went wrong, the reason. It is the same log the app shows under
**Settings → Logs**, available to an agent or a script so you can review
delivery in bulk instead of opening conversations one at a time.

| Tool | Endpoint | Does |
| - | - | - |
| `message_logs_list` | `GET $BASE/message-logs` | List messages with their status, recipient and failure reason |

Typical questions it answers: which numbers never received this week's send,
who has blocked us, and whether a specific customer got their message.

## Statuses are a lifecycle, not a verdict

`SENT`, `DELIVERED` and `READ` all mean the message went out. They differ only
in how far it got:

| Status | Means |
| - | - |
| `PENDING` | Not yet handed to the provider |
| `SENT` | It left Boom; no receipt has come back yet |
| `DELIVERED` | It reached the recipient's device |
| `READ` | The recipient opened it |
| `FAILED` | It could not be delivered — see `errorCode` / `errorMessage` |

<Note>
  To count what you sent, ask for `SENT`, `DELIVERED` and `READ` together.
  Asking for `SENT` alone returns only the ones still unconfirmed, which on a
  healthy account is a small fraction of what actually went out.
</Note>

**How far a message can get depends on the channel.** WhatsApp reaches `READ`.
Email never does — there is no read receipt, so an email resting at
`DELIVERED` is fully settled. And `PENDING` on an inbound message is normal and
permanent: nobody sends a receipt for a message a customer wrote to you.

## Why a message failed

On email, `errorMessage` carries the reason in words. On WhatsApp you usually
get a numeric `errorCode` instead:

| Code | Means |
| - | - |
| `63024` | The number is not reachable on WhatsApp — wrong number, or it never had an account |
| `63049` | The recipient's provider chose not to deliver a marketing message |
| `63032` | The recipient has blocked this business |
| `63050` | The business is not allowed to message this recipient |

Any other code is passed through unchanged. Rows sent before codes were
recorded have neither field; that is expected, not a data error.

## Filtering

```bash theme={null}
# What failed this week (the default window)
curl "$BASE/message-logs?status=FAILED" -H "Authorization: Bearer $BOOM_API_KEY"

# Everything sent since August, one page at a time
curl "$BASE/message-logs?status=SENT,DELIVERED,READ&createdAfter=2026-08-01" \
  -H "Authorization: Bearer $BOOM_API_KEY"

# One customer's history, any channel
curl "$BASE/message-logs?search=+5215500000001&channel=WHATSAPP,SMS" \
  -H "Authorization: Bearer $BOOM_API_KEY"
```

<Warning>
  To filter on **several** statuses or channels over HTTP, pass them as one
  comma-separated value (`?status=SENT,DELIVERED,READ`). Repeating the
  parameter (`?status=SENT&status=DELIVERED`) keeps only the last one and
  answers `200` with a narrower filter than you asked for.
</Warning>

`direction` defaults to `OUTBOUND` — a delivery status only means something for
a message you sent. Pass `direction=INBOUND` for what customers sent you.

## The window, and how to widen it

**With no dates, the read covers the last seven days** — a rolling window from
the moment you ask, so the edge is a timestamp rather than a midnight. Every
response says which window it used:

```json theme={null}
{
  "range": { "from": "2026-09-22T14:31:07.221Z", "to": null },
  "data": [ … ],
  "next_cursor": null
}
```

Quote that range alongside any count you report — "48 failures" means something
different over a week than over a year.

The default is not a ceiling. Name `createdAfter`, `createdBefore`, or both, and
that window is honoured however wide it is. Naming either one turns the default
off entirely, so `createdBefore=2026-08-01` really does mean everything before
August.

**A bare date means the whole day, in UTC.** `createdBefore=2026-09-30` includes
everything that happened on the 30th, not just the instant it began — so
`createdAfter=2026-09-01&createdBefore=2026-09-30` is the whole of that range,
with no missing final day. Pass a full timestamp
(`2026-09-30T06:00:00Z`) when you want an exact instant; it is used as given.

Once you start paging, the window is fixed. A `next_cursor` carries the range
its first page resolved, so `range` is identical on every page of one result
set even though the default floor is relative to "now".

<Note>
  There is no total. Results page through `next_cursor`, and a page comes back
  without counting the rows behind it — counting an unbounded log is far more
  expensive than reading a page of it. To total something up, page to the end.
</Note>

## What this log does not cover

It lists messages, so it is not a delivery-rate report. A few send failures
never create a row at all — an absent message is not proof that nothing was
attempted. Message text is never returned; the log answers what happened to a
message, not what it said.

## Related

* [One uniform surface](/one-surface) — the same capabilities over REST and MCP
* [Use MCP](/use-mcp) — connect an AI tool to your Boom organization
