> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getdial.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getdial.ai/_mcp/server.

# August 20, 2026

## Added

### Know when a message is delivered, rejected, or read

Sending a message told you Dial accepted it, and nothing more — the carrier's actual verdict arrived later with no way to hear about it. A new [`message.status_changed`](/api-reference/events/message-status-changed) event now publishes each advance on the account stream, so you can react the moment a message lands or is rejected instead of polling `GET /api/v1/messages`.

Delivery and reads are reported as **two independent axes**, because not every rail reports both. `deliveryState` covers `pending` → `delivered` | `undelivered` | `failed`, plus `unconfirmed` for rails that send no delivery receipts. `readState` covers `unread` → `read`, plus `unsupported` for rails that never report reads. Each event names which axis moved in `changed` and carries the current value of both, so one event is always a complete picture. Failures carry a plain-language `deliveryError`.

Every field is flat, so the stream is directly filterable — `dial wait-for message.status_changed -f deliveryState=failed` waits for a rejection. The event is also subscribable as a [webhook](/documentation/platform/webhooks).

`GET /api/v1/messages` gains the matching `deliveryState`, `readState`, `readAt`, and `deliveryError` fields, so a fetch and a stream event never disagree.

## Changed

### `status` is delivery-only; reads moved to `readState`

A message's `status` no longer returns `read`. Reads now live on the independent `readState` and `readAt` fields, which means a message can be reported as both delivered **and** read — previously one value had to stand for both, so a read receipt hid the delivery confirmation. A read message now reports `status: "delivered"` with `readState: "read"`.

`status` and `statusError` keep working and still describe delivery only. `statusError` is identical to the new `deliveryError`. `status` is the same axis as `deliveryState` in the older vocabulary, differing on two values: `sent` is `pending`, and `unknown` is `unconfirmed`. If you branch on `status === "read"`, move to `readState === "read"`.