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

# Message lifecycle

> Statuses for scheduled and sent messages, what you can do in each state, and which webhook fires next.

Every outbound message moves through a small state machine. Knowing the status tells you what you can still do and which webhook to expect.

```
schedule (optional)          send
pending ──► sent|failed|expired|cancelled
                │
                ▼
            queued ──► sent ──► delivered ──► read / played
                         │
                         └──► failed
```

## Scheduled message statuses

When you pass `send_at` on [`POST /v1/messages`](https://docs.lazyapp.in/sending/messages), LazyApp stores a scheduled row and returns `202` instead of sending immediately.

| Status | Meaning |
| - | - |
| `pending` | Waiting for `send_at` (or still retrying after a transient failure) |
| `sent` | Dispatched; the linked message now carries delivery status |
| `failed` | Could not be sent after attempts |
| `expired` | Past `ttl_seconds` / expiry before it could go out |
| `cancelled` | Cancelled with `DELETE /v1/messages/scheduled/{id}` |

List with [`GET /v1/messages/scheduled`](https://docs.lazyapp.in/api-reference/messages/list-scheduled-messages). Cancel while `pending`.

## Message statuses

After accept (or after a schedule fires), the message row uses:

| Status | Meaning | Typical webhook |
| - | - | - |
| `queued` | Accepted; waiting on the provider path | — |
| `sent` | Provider accepted it | `message.sent` |
| `delivered` | Reached the contact's device | `message.delivered` |
| `read` | Contact opened it | `message.read` |
| `played` | Contact played voice/video note | `message.read` / media-specific |
| `failed` | Terminal failure | `message.failed` |

`POST /v1/messages` returning `201` means **accepted**, not delivered. Poll [`GET /v1/messages/{uuid}`](https://docs.lazyapp.in/api-reference/messages/retrieve-a-message) only for debugging; subscribe to [webhooks](https://docs.lazyapp.in/webhooks/events.md) in production.

## Which status a new message gets

| You send | Initial result |
| - | - |
| No `send_at` | Message created; response is `201` with status typically `queued` or already `sent` / `failed` if the provider answered synchronously |
| `send_at` in the future | Scheduled row; response is `202` with status `pending` |

There is no draft or multi-platform `partial` state — one send targets one WhatsApp number and one contact.

## What you can do

| Operation | When |
| - | - |
| Cancel schedule | While the scheduled row is `pending` |
| Fetch message | Any time after accept, by LazyApp uuid |
| Download inbound media | After `message.received` with media |
| Retry a failed *schedule* | Create again with a new `Idempotency-Key` / body key — there is no separate retry endpoint |
| Unpublish / edit delivered | Not supported by WhatsApp Cloud API for business-sent messages |

There is no unpublish: WhatsApp does not let you delete another person's delivered message via the business API the way some social networks do. Contacts can delete for themselves; you may see `message.deleted` when coexistence reports a revoke.

## Tracking with webhooks

| Event | Fires when |
| - | - |
| `message.sent` | Provider accepted the outbound message |
| `message.delivered` | Delivered to the device |
| `message.read` | Opened (or played, for some media) |
| `message.failed` | Terminal failure; read `error_message` |
| `message.received` | Inbound from a contact |
| `message.deleted` | Revoke / delete reported |

See [Webhook events](https://docs.lazyapp.in/webhooks/events.md) for payload shapes.

## Related

* [Scheduling and timezones](https://docs.lazyapp.in/sending/messages.md#scheduling) — how `send_at` is interpreted
* [Idempotency](https://docs.lazyapp.in/concepts/idempotency.md) — safe retries on create
* [Errors](https://docs.lazyapp.in/concepts/errors.md) — refusals vs async failure


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.