# Eventos

Publique eventos e entregue-os aos assinantes por webhook, consulta ou transmissão ao vivo, com mensagens mortas e reenvio.

URL base: `https://events.inovacc.dev`

## Visão geral

Eventos permite que uma parte do seu sistema anuncie que algo aconteceu, e que cada parte interessada fique sabendo, sem que as duas se conheçam. Um publicador envia um evento como `order.paid` para `https://events.inovacc.dev`; toda **assinatura** cujo padrão corresponda ao tipo do evento o recebe, por **webhook** no seu endpoint HTTPS, por **pull** a partir do seu worker, ou por **transmissão ao vivo** por um WebSocket. Os eventos que não podem ser entregues são mantidos como **mensagens mortas**, que você pode inspecionar e reenviar.

O problema que ele resolve é a distribuição confiável. Chamar cada serviço interessado diretamente os acopla, perde mensagens quando um deles está fora do ar e faz novas tentativas de forma ruim. Eventos aceita a publicação uma vez, a entrega pelo menos uma vez a cada assinatura, repete as entregas que falharam com espera crescente, deduplica publicações repetidas e guarda o que não conseguiu entregar.

Use Eventos para reagir a mudanças: enviar um e-mail quando um pedido é pago, atualizar um cache quando um registro muda, enviar uma notificação a uma página. Mantenha o próprio estado em [Dados](/pt-br/products/data/) e os bytes em [Arquivos](/pt-br/products/files/): um evento deve dizer *o que aconteceu*, não carregar o objeto. Para a história completa do receptor de webhooks, veja o [guia de Webhooks](/pt-br/guides/webhooks/).

## Conceitos

**Espaço de trabalho.** Toda rota fica sob `/v1/workspaces/{workspace}`. O espaço de trabalho no caminho é uma declaração conferida contra a sua credencial: uma chave de aplicação só pode nomear o seu próprio espaço de trabalho, e qualquer outro responde `403 forbidden`, igual a um que não existe.

**Eventos e o envelope.** Você publica `type` (palavras minúsculas separadas por pontos, como `invoice.created`, até 128 bytes), `data` (qualquer valor JSON) e uma `idempotency_key` opcional. Os assinantes recebem um envelope com `event_id` (`evt_` e 26 caracteres, atribuído pela Inovacc), `type`, `source` (a sua aplicação, definida a partir da credencial), `time`, `tenant`, `idempotency_key` e `data`.

**Níveis.** Uma publicação pode nomear um `tier`: `basic` (o padrão), `reliable` ou `advanced`. O nível define o maior `data` que um evento pode carregar: 16 KiB para `basic` e `reliable`, 32 KiB para `advanced`.

**Assinaturas e padrões.** Uma assinatura lista de 1 a 20 padrões em `types`, cada um sendo `*` (tudo), um tipo exato ou um prefixo terminado em `.*` (`order.*` corresponde a `order.paid` e `order.item.added`), e um modo de entrega.

**Modos de entrega.** `webhook` envia cada evento por POST à sua URL HTTPS, assinado com o segredo da assinatura. `pull` mantém uma caixa de entrada que o seu código lê e confirma. `websocket` mantém a mesma caixa de entrada e também a transmite ao vivo; o pull continua funcionando ao lado da transmissão.

**Pelo menos uma vez.** Cada evento chega a cada assinatura correspondente uma vez em operação normal, mas uma nova tentativa ou uma confirmação perdida pode entregá-lo de novo. Os receptores deduplicam por `event_id`.

**Mensagens mortas.** Uma entrega por webhook que continua falhando é movida para as mensagens mortas do seu espaço de trabalho, com o último erro e o número de tentativas, e mantida por 30 dias.

## Como funciona

Toda chamada leva `Authorization: Bearer <credential>`. A credencial é o tenant: organização, conta e aplicação vêm dela, e nada em um corpo pode nomear outra. Uma **chave secreta** é para servidores e pode usar todas as rotas. Uma **chave publicável** (`apb_...`) só pode abrir uma transmissão, a partir de uma origem que a sua aplicação lista. O token de um usuário final não pode usar nada. Cada rota exige a sua própria permissão, como `events:event.publish`, `events:subscription.write` ou `events:dead.replay`.

**Publicar.** `POST /events` com `{"event": {...}}` ou `{"events": [...]}` (de 1 a 100). A resposta é `202` com `accepted` (cada um com o seu `event_id` e `duplicate`) e `rejected` (cada um com um índice e um código); um evento inválido nunca bloqueia os outros. Enviar uma `idempotency_key` já usada no espaço de trabalho nas últimas 168 horas devolve o `event_id` original com `duplicate: true` e não entrega nada de novo.

**Entrega por webhook.** A Inovacc envia o envelope por POST à sua URL com `x-event-id`, `x-event-type`, `x-event-timestamp` e `x-event-signature: v1=<hex>`, um HMAC-SHA256 do carimbo de data e hora e do corpo bruto. Qualquer `2xx` em até 10 segundos é uma entrega; qualquer outra coisa é repetida após 10 segundos, com o atraso dobrando até 10 minutos, até que o limite de 5 tentativas seja atingido e o evento se torne uma mensagem morta. Redirecionamentos nunca são seguidos, e a sua URL deve ser HTTPS pública na porta 443.

**Pull.** `POST /subscriptions/{id}/pull` com `max` (de 1 a 100, padrão 10) e `lease_seconds` (de 5 a 300, padrão 30) devolve as mensagens mais antigas, cada uma com o seu `delivery_count`. Uma mensagem obtida fica oculta durante o seu período de reserva; confirme-a com `POST .../ack` e os ids, ou ela volta quando a reserva termina.

**Transmissão.** `GET /subscriptions/{id}/stream` faz o upgrade para um WebSocket com o subprotocolo `inovacc.v1`; um navegador passa a sua chave como um segundo subprotocolo, `bearer.<key>`. Cada quadro é um envelope; responda `{"ack": [...]}` para confirmar. Um quadro não confirmado é enviado de novo após a sua reserva.

**Monitoramento.** `GET /stats` devolve `published`, `completed`, `depth`, `oldest_pending_age_seconds`, `retries`, `retry_rate` e `dlq_inflow` do espaço de trabalho. Toda chamada é registrada no [Registro de atividade](/pt-br/products/activity/). O que é medido é o evento entregue.

## Primeiros passos

Você precisa de uma chave secreta vinculada ao seu espaço de trabalho com as permissões de eventos ([Autenticação](/pt-br/guides/authentication/)).

1. **Crie uma assinatura pull.** `POST /v1/workspaces/{workspace}/subscriptions` com `{"types":["demo.*"],"delivery":{"mode":"pull"}}` (veja [os exemplos](#example)). A resposta é `201` com um `subscription_id`.
2. **Publique.** `POST .../events` com `{"event":{"type":"demo.hello","data":{"n":1}}}`. A resposta é `202` com uma entrada em `accepted` e o seu `event_id`.
3. **Faça o pull.** `POST .../subscriptions/{id}/pull` com `{}`. O seu evento está em `messages`, com `delivery_count` 1.
4. **Confirme.** `POST .../subscriptions/{id}/ack` com `{"ids":["<event_id>"]}` responde `{"acked":1}`; um segundo pull vem vazio.
5. **Adicione um webhook.** Crie uma segunda assinatura com `{"mode":"webhook","url":"https://<your endpoint>"}`, guarde o `signing_secret` que a resposta mostra uma única vez e siga o [guia de Webhooks](/pt-br/guides/webhooks/) para verificar as entregas.

## Casos de uso

**Atendimento de pedidos.** O checkout publica `order.paid` com o id do pedido em `data` e o id do pagamento como `idempotency_key`. Uma assinatura por webhook para `order.*` chega ao sistema do depósito; uma assinatura pull alimenta a tarefa de faturamento. Um checkout repetido pelo cliente publica uma única vez.

**Atualizações ao vivo em uma página.** Um painel abre uma transmissão em uma assinatura `websocket` para `ticket.*` com uma chave publicável a partir da sua própria origem, mostra cada novo chamado à medida que o quadro chega e o confirma.

**Recuperação após uma queda.** O endpoint de um parceiro ficou fora do ar por uma hora. Suas entregas terminaram em mensagens mortas com `last_error` `webhook_timeout`. Quando ele volta, a sua equipe lista `GET /dead` e reenvia cada evento; ele vai apenas para as assinaturas que nunca o receberam.

## Limites e preços

| Limite | Valor |
|---|---|
| Eventos por publicação | 100 |
| Corpo da requisição | 5 MiB |
| `data` por evento | 16 KiB (`basic`, `reliable`), 32 KiB (`advanced`) |
| Janela de deduplicação de `idempotency_key` | 168 horas |
| Assinaturas por espaço de trabalho; padrões por assinatura | 50; 20 |
| Mensagens por pull; reserva | 100; 5 a 300 segundos |
| Tempo de resposta do webhook | 10 segundos |
| Novas tentativas | a partir de 10 segundos, dobrando até 10 minutos; limite 5, depois mensagem morta |
| Mensagens mortas mantidas | 30 dias |
| Quadro de transmissão vindo do cliente | 49.152 bytes |
| Taxa | 600 chamadas por minuto por credencial, por localização |

**Preços:** sob consulta. A unidade de preço é o **evento entregue**.

## Erros

| Status | Código | O que significa e o que fazer |
|---|---|---|
| 400 | `invalid_body` | O corpo está malformado, ou nomeia `source` (ele vem da sua credencial). |
| 400 | `invalid_query` | Apenas `GET /dead` aceita uma consulta (`limit`). |
| 400 | `invalid_subscription`, `invalid_delivery`, `invalid_limit`, `invalid_id` | Corrija a assinatura, a entrega ou o parâmetro. |
| 400 | `invalid_url`, `url_not_https`, `url_has_credentials`, `url_port_not_allowed`, `url_destination_blocked` | A URL do webhook deve ser HTTPS pública na porta 443, sem credenciais. |
| 401 | `invalid_credentials` | Envie uma credencial válida. |
| 403 | `forbidden`, `account_required` | Falta permissão ou o espaço de trabalho está errado; use uma chave de aplicação. |
| 403 | `publishable_key_not_allowed`, `origin_not_allowed`, `secret_key_in_browser` | Uma chave publicável só pode transmitir, a partir de uma origem listada; mantenha as chaves secretas em servidores. |
| 404 | `subscription_not_found`, `dead_event_not_found` | Não existe no seu espaço de trabalho. |
| 409 | `wrong_delivery_mode`, `too_many_subscriptions`, `secret_not_managed` | A operação não se encaixa no modo de entrega, ou você está em 50 assinaturas. |
| 413 | `payload_too_large` | O corpo excede 5 MiB. |
| 426 | `upgrade_required` | A rota de transmissão exige um upgrade para WebSocket. |
| 429 | `rate_limited`, `too_many_streams` | Diminua o ritmo; feche os sockets de que não precisa. |
| 503 | `service_unavailable`, `events_disabled`, `signing_unavailable` | Tente novamente mais tarde; uma publicação repetida com as mesmas chaves reutiliza seus ids. |

Um evento rejeitado dentro de um `202` traz o seu próprio código: `invalid_event`, `unknown_field`, `invalid_type`, `invalid_idempotency_key`, `payload_too_large` ou `data_required`.

## Boas práticas

- **Envie uma `idempotency_key`** derivada do que aconteceu (o id do pagamento, a versão do registro), para que uma publicação repetida não seja um segundo evento.
- **Deduplique por `event_id`** em todo receptor: a entrega é de pelo menos uma vez.
- **Coloque ids em `data`, não objetos**: busque o estado atual em [Dados](/pt-br/products/data/) quando tratar o evento.
- **Responda aos webhooks rapidamente** com um `2xx` e faça o trabalho depois; 10 segundos é o limite.
- **Verifique a assinatura de todo webhook** antes de interpretar o corpo ([guia de Webhooks](/pt-br/guides/webhooks/)).
- **Confirme as mensagens obtidas por pull** depois de processá-las, e escolha uma reserva maior que o seu tempo de processamento.
- **Acompanhe `depth` e `dlq_inflow`** em `/stats`, e reenvie as mensagens mortas depois que a causa for corrigida.

## Relacionados

- [Guia de Webhooks](/pt-br/guides/webhooks/): receba, verifique e reenvie entregas
- [Dados](/pt-br/products/data/), [Registro de atividade](/pt-br/products/activity/)
- [Autenticação](/pt-br/guides/authentication/), [Erros](/pt-br/guides/errors/), [Limites](/pt-br/guides/limits/)

## Endpoints

- POST /v1/workspaces/{workspace_id}/events
- GET /v1/workspaces/{workspace_id}/subscriptions
- POST /v1/workspaces/{workspace_id}/subscriptions
- DELETE /v1/workspaces/{workspace_id}/subscriptions/{subscription_id}
- POST /v1/workspaces/{workspace_id}/subscriptions/{subscription_id}/rotate-secret
- POST /v1/workspaces/{workspace_id}/subscriptions/{subscription_id}/pull
- POST /v1/workspaces/{workspace_id}/subscriptions/{subscription_id}/ack
- GET /v1/workspaces/{workspace_id}/subscriptions/{subscription_id}/stream
- GET /v1/workspaces/{workspace_id}/dead
- POST /v1/workspaces/{workspace_id}/dead/{event_id}/replay
- GET /v1/workspaces/{workspace_id}/stats

## Exemplo

```sh
curl -X POST "https://events.inovacc.dev/v1/workspaces/{workspace_id}/events" -H "Authorization: Bearer $INOVACC_API_KEY" -H "Content-Type: application/json" -d '{}'
```
