# Остановка лида

Остановите автоматическую цепочку одного лида. Новые попытки запрещаются, но уже начатая отправка еще может завершиться.

> **Важно.** Доступ к записи включается для кабинета по запросу.

## Запрос

`POST /v1/leads/{id}/stop` требует `leads:stop` и доступа к пространству и кампании лида. `id` обозначает участие в одной кампании: стоп не останавливает того же человека в других кампаниях и не добавляет его в общий стоп-лист.

Передайте обязательный `Idempotency-Key` (от 1 до 255 печатных ASCII-символов), `Content-Type: application/json` и пустой объект `{}`. Дополнительные поля запрещены. Сохраните уникальный ключ этой операции до запроса; ключ добавления лидов для стопа повторно не используйте.

```bash
curl --retry 5 --retry-max-time 300 \
  -X POST https://api.melioralab.io/v1/leads/5a7c3e92-1f4b-4d68-9c2e-7b0a6d4f8e31/stop \
  -H "Authorization: Bearer $MELIORA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $MELIORA_IDEMPOTENCY_KEY" \
  --data-raw '{}'
```
```python
import os
import random
import time
import requests

headers = {
    "Authorization": f"Bearer {os.environ['MELIORA_API_KEY']}",
    "Content-Type": "application/json",
    "Idempotency-Key": os.environ["MELIORA_IDEMPOTENCY_KEY"],
}
for attempt in range(6):
    response = requests.post(
        "https://api.melioralab.io/v1/leads/5a7c3e92-1f4b-4d68-9c2e-7b0a6d4f8e31/stop",
        headers=headers, data=b"{}", timeout=15,
    )
    if response.status_code in (429, 503) and attempt < 5:
        time.sleep(float(response.headers.get("Retry-After", "5")) + random.random())
        continue
    response.raise_for_status()
    command = response.json()
    print(command["id"], command["result"])
    break
```
```js
const headers = {
  Authorization: `Bearer ${process.env.MELIORA_API_KEY}`,
  'Content-Type': 'application/json',
  'Idempotency-Key': process.env.MELIORA_IDEMPOTENCY_KEY,
};
for (let attempt = 0; attempt < 6; attempt++) {
  const response = await fetch('https://api.melioralab.io/v1/leads/5a7c3e92-1f4b-4d68-9c2e-7b0a6d4f8e31/stop', {
    method: 'POST', headers, body: '{}', signal: AbortSignal.timeout(15_000),
  });
  if ([429, 503].includes(response.status) && attempt < 5) {
    const delay = Number(response.headers.get('Retry-After') ?? 5) + Math.random();
    await response.body?.cancel();
    await new Promise((resolve) => setTimeout(resolve, delay * 1000));
    continue;
  }
  if (!response.ok) throw new Error(`Meliora API ${response.status}: ${await response.text()}`);
  const command = await response.json();
  console.log(command.id, command.result);
  break;
}
```

## Что означает ответ

HTTP 202 возвращает [команду](https://docs.melioralab.io/reference/commands/) с `kind=stop`, целевым `lead_id`, пустым `items` и полями результата:

- `result.automation_state=stopped`: запрет новых автоматических попыток установлен, уже начатой отправки нет.
- `result.automation_state=stopping`: запрет установлен, но отправка уже началась.
- `result.send_in_flight=true`: эта отправка еще может завершиться. Это не подтверждение ее доставки.
- `result.unknown_outcome_message_ids`: непрозрачные идентификаторы отправок, исход которых неизвестен. Не разбирайте их формат.
- `cleanup_pending=true`: еще остается уборка остановленных задач. Это поле команды, а не `result`.

Команда может сразу завершиться как `done` или `done_with_unknown`, а может оставаться `accepted` или `running`. Опрашивайте [`GET /v1/commands/{id}`](https://docs.melioralab.io/commands/) до конечного состояния и завершения уборки. `stopped` в результате само по себе не заменяет эту проверку.

> **Внимание.** Стоп не отзывает уже отправленное сообщение. `done_with_unknown` означает: остановка завершена, новых попыток не будет, но исход прошлой попытки не установлен. Не отправляйте сообщение повторно из-за неизвестного исхода.

При [чтении лида](https://docs.melioralab.io/reference/leads/) смотрите `automation_stopped`, `automation_stopped_at`, `automation_stop_source` и объект `automation`: `state` (`running`, `stopping`, `stopped`, `unknown`), `stopped_at`, `source`, `send_in_flight`, `unknown_outcome_message_ids`. В ответе чтения `automation_stopped`, `automation.send_in_flight` и `automation.unknown_outcome_message_ids` могут быть `null`, если данные не установлены. `null` не означает `false` или пустой массив. Статус участия, например `replied`, не обязан меняться при стопе.

`automation.state=running` означает отсутствие установленного стопа на уровне лида, а не разрешение немедленно отправлять: пауза кампании и удержания все еще действуют. В отличие от чтения, результат команды стопа содержит определенный `send_in_flight` (`true` или `false`) и массив `unknown_outcome_message_ids`.

## Событие и повтор

`lead.stopped` появляется в [ленте событий](https://docs.melioralab.io/events-polling/) после завершения начатой отправки и уборки, а не сразу при `stopping`. В `data.stop` приходят `automation_stopped`, `stopped_at` и `source`. Схема допускает `true`, `false` или `null` для `automation_stopped`, время или `null` для `stopped_at`, строку или `null` для `source`. Для установленного API-стопа ожидаются `true` и `api`, но это не константы схемы. Событие подтверждает остановку автоматизации, но не определенность всех прошлых отправок.

Повтор с тем же ключом, лидом и телом возвращает ту же команду с текущим состоянием. При 429/503 выдержите `Retry-After`, при обрыве сохраните тот же ключ. Повторная остановка с новым ключом тоже безопасна: цепочка не возобновляется, исходное время стопа сохраняется, второго перехода и второго события `lead.stopped` не создается. Другой лид или операция с уже использованным ключом дают 422 `idempotency_key_reused`.

## Бюджет и ошибки

Стоп имеет вес 1 и отдельный бюджет: до 2 одновременных запросов, 60 в минуту на ключ и 120 в минуту на кабинет. Он не расходует обычный минутный бюджет кабинета и его обычные места одновременных запросов. Лимиты не гарантируют немедленный ответ; при 429/503 повторяйте после паузы.

403 `scope_required` означает нехватку права; 403 `api_write_disabled` означает, что запись еще не включена. Неверное тело дает 422 `invalid_request`, неверный заголовок 400 `invalid_parameter`, чужой или недоступный лид 404 `not_found`. При 503 `rate_limiter_unavailable` повторите исходный запрос с тем же ключом. Подробности в [ошибках](https://docs.melioralab.io/errors/) и [лимитах](https://docs.melioralab.io/rate-limits/).
