# Lead and delivery statuses

A lead has four state fields. status tells you where the lead is in the campaign. delivery_state and automation_state tell you what is happening with sending. reason explains why the sequence is waiting.

## How to read the states

- `status`: the outcome of the lead's participation in the campaign. It changes rarely: messaged, replied, opted out.
- `delivery_state`: what is happening to the next unsent message in the sequence. If there is none, it describes the last sent message.
- `automation_state`: whether the sequence is running for this lead.
- `reason`: why the sequence is waiting and, when known, when the next attempt is expected.

If there is not enough data, or sources contradict each other, the API returns `unknown` instead of guessing. This is a normal state, not an error.

## Lead status

| `status` | Meaning |
|---|---|
| `new` | The lead was added to the campaign. No messages yet. |
| `contacted` | The first message was sent. No reply yet. |
| `completed` | The sequence finished without a reply. |
| `replied` | The lead replied. |
| `blocked` | This contact can't be messaged, or we chose not to. For example, the recipient doesn't exist, doesn't accept messages, was already messaged, or is on the stop list. |
| `failed` | Sending failed. |
| `suspended` | Work with the lead is temporarily paused, for example while the sender account is resting. |
| `deleted` | The lead was removed from the campaign. |
| `opted_out` | The lead asked not to be messaged again. |
| `unknown` | The status could not be determined. |

## Delivery state

| `delivery_state` | Meaning | Now |
|---|---|---|
| `queued` | The message is queued for sending. | Returned |
| `held` | Sending is held until someone makes a decision in Meliora. | Returned |
| `sent` | Sending is confirmed. No further messages are expected, or they are not scheduled yet. | Returned |
| `unknown` | The outcome is unknown. For example, the send attempt was not confirmed or there is not enough data. | Returned |
| `waiting_queue` | The next message is expected but not queued yet. | Not returned yet |
| `failed` | Sending failed permanently and no retry is scheduled. | Not returned yet |

`unknown` does not mean failure. Don't resend a message to the lead on your own just because you got `unknown`: the message may have been delivered.

## Sequence state

| `automation_state` | Meaning | Now |
|---|---|---|
| `running` | The sequence is running: the campaign is active and sending is allowed. The sending window, the daily quota and Telegram pauses only delay the next message. | Returned |
| `paused` | The campaign is paused, not launched yet, or stopped because of the subscription. | Returned |
| `held` | The sequence is held until someone makes a decision in Meliora. | Returned |
| `stopped` | The sequence is stopped: the lead replied, opted out or was stopped, or the campaign is stopped or archived. | Returned |
| `unknown` | The state could not be determined. | Returned |
| `completed` | All steps of the sequence are done. | Not returned yet |

For leads with no message currently scheduled, such as leads who replied or finished the sequence, `automation_state` often comes back as `unknown` for now. Check `status` for the outcome.

> **Note.** Values marked "Not returned yet" are in the schema but are not returned during early access: those leads come back with `unknown`. Filtering `GET /v1/leads?delivery_state=failed` or `waiting_queue` currently returns an empty list. We will announce in the [Changelog](https://docs.melioralab.io/en/changelog/) when these values start being returned.

## Wait reason

Both leads and campaigns have a `reason` object:

```json
{
  "reason_code": "telegram_wait",
  "reason_observed_at": "2026-10-27T08:41:07.000Z",
  "next_attempt_at": "2026-10-27T09:26:00.000Z",
  "requires_customer_action": false
}
```

| `reason_code` | Meaning | Now |
|---|---|---|
| `none` | The sequence is not waiting for anything. | Returned |
| `daily_limit` | The sender account has used up its daily quota. | Returned |
| `queue_pending` | The message is waiting its turn to be sent. | Returned |
| `telegram_wait` | Telegram asked the account to wait before sending again. | Returned |
| `account_unavailable` | The sender account is unavailable. | Returned |
| `account_restricted` | Telegram restricted the sender account. | Returned |
| `campaign_paused` | The campaign is paused or not launched yet. | Returned |
| `subscription_inactive` | The subscription is inactive or credits ran out. | Returned |
| `manual_hold` | Sending is held until someone makes a decision in Meliora. | Returned |
| `automation_stopped` | The sequence is stopped. | Returned |
| `unknown` | The reason is unknown. | Returned |
| `schedule_window` | Waiting for the sending window set in the campaign schedule. | Not returned yet |
| `service_unavailable` | The reason could not be read right now. | Not returned yet |

- `next_attempt_at` is set only when the time is known. Otherwise it is `null`.
- `requires_customer_action: true` means the sequence won't continue until someone acts in Meliora, for example launches the campaign or renews the subscription.
- API rate limits (a 429 response) don't affect `reason`. They limit your requests, not message sending.

## Campaign status

| `status` | Meaning |
|---|---|
| `draft` | Draft. |
| `scheduled` | Launch is scheduled. |
| `launching` | The campaign is launching. |
| `active` | The campaign is running. |
| `paused` | Paused. |
| `paused_no_credits` | Paused: out of credits. |
| `stopped` | Stopped. |
| `completed` | Completed. |
| `archived` | Archived. |
| `failed` | Launch or run error. |
| `unknown` | The status could not be determined. |

## Timestamps

- `first_message_sent_at`: when the first message was sent. If sending is not confirmed, the field is `null`, even when the status is `contacted`.
- `first_reply_at`: when the first reply arrived.
- `created_at`: when the lead was added to the campaign. It never changes.
- All timestamps are in UTC with milliseconds, for example `2026-10-26T07:30:00.000Z`.
- `observed_at`: when the server read the data for this response. It is not the time the lead last changed.
