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

# Webhooks de conclusão

> Seja avisado quando um run termina, com entregas assinadas e verificáveis

Em vez de consultar em loop, deixe a Leme chamar você. Passe um `callbackUrl` ao [criar um run](/pt-BR/api/runs#criar-um-run) e a Leme envia um `POST` assinado para essa URL quando o run chega a um estado final — ou pausa para aprovação.

```json theme={null}
{
  "input": { "message": "Qualifique este lead." },
  "callbackUrl": "https://example.com/hooks/leme"
}
```

A URL precisa ser HTTPS pública na porta padrão. URLs apontando para redes privadas são rejeitadas com `422 callback_url_invalid`.

## Eventos

| Evento                 | Enviado quando                           |
| ---------------------- | ---------------------------------------- |
| `run.completed`        | O run terminou com sucesso               |
| `run.failed`           | O run falhou                             |
| `run.canceled`         | O run foi cancelado                      |
| `run.waiting_approval` | O run pausou aguardando aprovação humana |

O payload é propositalmente enxuto — identificadores e status, nunca a resposta do agent:

```json theme={null}
{
  "type": "run.completed",
  "timestamp": "2026-07-07T18:00:00Z",
  "data": {
    "runId": "run_id",
    "agentId": "agent_id",
    "sessionId": "session_id",
    "status": "completed",
    "origin": "api",
    "metadata": { "leadId": "4821" }
  }
}
```

Ao recebê-lo, busque o resultado com [`GET /api/v1/runs/:id`](/pt-BR/api/runs#acompanhar-o-run). Isso mantém conteúdo sensível fora do seu endpoint de webhook e garante que você sempre leia o estado mais recente.

## Verificar a assinatura

Toda entrega é assinada seguindo a especificação [Standard Webhooks](https://www.standardwebhooks.com), o mesmo esquema usado por OpenAI e Svix. Três headers acompanham a requisição:

| Header              | Conteúdo                                                    |
| ------------------- | ----------------------------------------------------------- |
| `webhook-id`        | ID único da entrega, estável entre retentativas             |
| `webhook-timestamp` | Timestamp Unix (segundos) do envio                          |
| `webhook-signature` | Uma ou mais assinaturas `v1,<base64>`, separadas por espaço |

Primeiro, obtenha o secret de assinatura do projeto (exige token `read_write`):

```bash theme={null}
curl https://app.leme.ai/api/v1/project/webhook-signing-secret \
  -H "Authorization: Bearer leme_pat_v1_..."
```

Depois, verifique com qualquer biblioteca Standard Webhooks:

<CodeGroup>
  ```js Node theme={null}
  import { Webhook } from "standardwebhooks"

  const webhook = new Webhook(secret) // "whsec_..."

  // Lança erro se a assinatura for inválida ou antiga demais
  webhook.verify(rawBody, {
    "webhook-id": req.headers["webhook-id"],
    "webhook-timestamp": req.headers["webhook-timestamp"],
    "webhook-signature": req.headers["webhook-signature"],
  })
  ```

  ```python Python theme={null}
  from standardwebhooks import Webhook

  webhook = Webhook(secret)  # "whsec_..."

  # Lança erro se a assinatura for inválida ou antiga demais
  webhook.verify(raw_body, {
      "webhook-id": headers["webhook-id"],
      "webhook-timestamp": headers["webhook-timestamp"],
      "webhook-signature": headers["webhook-signature"],
  })
  ```
</CodeGroup>

<Warning>
  Verifique sempre contra o **corpo bruto da requisição**, antes de qualquer
  parse de JSON. Rejeite entregas cuja assinatura não bater.
</Warning>

## Rotacionar o secret

Se o secret vazar — ou no seu ciclo normal de rotação — gere um novo:

```bash theme={null}
curl -X POST https://app.leme.ai/api/v1/project/webhook-signing-secret \
  -H "Authorization: Bearer leme_pat_v1_..."
```

O secret anterior continua assinando por 24 horas, para você fazer a transição sem perder entregas. Durante essa janela, `webhook-signature` carrega duas assinaturas — a entrega é válida se **qualquer** uma bater, o que toda biblioteca Standard Webhooks já trata por você.

## Retentativas e confiabilidade

Entregas são **at least once**. Se o seu endpoint não responder `2xx` em 15 segundos, a Leme tenta de novo: após 1 minuto, 5 minutos, 30 minutos e 2 horas. Depois de cinco tentativas falhas, a entrega é marcada como esgotada.

Como retentativas podem se sobrepor ao seu processamento, torne seu handler idempotente — deduplique pelo `webhook-id`.

Você pode inspecionar o estado da entrega a qualquer momento no recurso do run:

```json theme={null}
{
  "callback": {
    "url": "https://example.com/hooks/leme",
    "lastStatus": "delivered",
    "attempts": 1
  }
}
```

## Boas práticas

* **Responda rápido.** Confirme com `2xx` imediatamente e processe de forma assíncrona; o timeout de 15 segundos inclui o seu handler.
* **Deduplique pelo `webhook-id`.** Retentativas reutilizam o mesmo ID.
* **Não confie só no payload.** Verifique a assinatura e busque o run pela API para obter o estado autoritativo.
* **Fique de olho em entregas esgotadas.** Se esgotaram, seu endpoint ficou fora do ar por horas — consulte os runs em andamento para se atualizar.
