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

# Recebendo eventos do Midaz

> Escolha entre vincular diretamente aos exchanges do RabbitMQ do Midaz ou assinar via Streaming Hub para consumir eventos do ledger.

O Midaz emite um fluxo de eventos de domínio sempre que algo relevante acontece no ledger — uma transação é registrada, um saldo entra em overdraft, uma conta é criada. Se o seu sistema precisa reagir a essas mudanças (conciliação, notificações, projeções derivadas, analytics), você pode consumir esse fluxo de eventos em vez de fazer polling na API REST.

Este guia cobre as duas formas suportadas de receber eventos do Midaz e como escolher entre elas:

1. **RabbitMQ direto** — vincule sua própria fila aos exchanges AMQP do Midaz.
2. **Streaming Hub** — assine via serviço gerenciado de fan-out da Lerian.

## As duas abordagens em resumo

***

O Midaz publica cada evento de domínio em **dois transportes** em paralelo:

* Uma publicação no **RabbitMQ (AMQP)** para topic exchanges gerenciados pelo deployment do Midaz.
* Uma publicação em um **barramento de eventos interno**, que o Streaming Hub consome e distribui (fan-out) para assinantes por tenant.

Isso te dá duas superfícies de integração para os *mesmos* eventos subjacentes:

| Evento            | RabbitMQ direto                      | Streaming Hub                                        |
| ----------------- | ------------------------------------ | ---------------------------------------------------- |
| Transporte        | AMQP 0-9-1                           | Webhook / Pull (HTTP) / SQS / RabbitMQ / EventBridge |
| Você se conecta a | O broker do Midaz                    | Uma API REST do plano de controle                    |
| Acoplamento       | Forte (infra do Midaz)               | Fraco (edge gerenciado)                              |
| Ideal para        | Deployments self-hosted / co-locados | Integradores externos / SaaS                         |

<Note>
  Se você não opera seu próprio broker do Midaz, use o Streaming Hub. RabbitMQ direto é para integradores que rodam dentro ou ao lado do deployment do Midaz e são donos do broker.
</Note>

## Opção A — RabbitMQ direto

***

### Como funciona

O ledger do Midaz publica eventos por meio de um produtor interno para um conjunto de **topic exchanges**. Um consumidor vincula sua própria fila ao exchange correspondente com um padrão de routing key e consome via AMQP.

Fatos do publicador (do serviço de ledger):

* **Content type:** `application/json`
* **Delivery mode:** persistente
* **Headers:** o contexto de trace do OpenTelemetry é injetado em cada mensagem
* **Tenancy:** o produtor é multi-tenant — as mensagens são publicadas em um **vhost específico por tenant**

Exchanges de eventos e suas routing keys:

| Exchange                                  | Padrão de routing key                                               | Emite                                     |
| ----------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------- |
| `transaction.transaction_events.exchange` | `midaz.transaction.<STATUS>` (ex.: `midaz.transaction.APPROVED`)    | Envelopes do ciclo de vida de transações  |
| `transaction.overdraft_events.exchange`   | `midaz.balance.overdraft.<action>` (`drawn` / `repaid` / `cleared`) | Eventos de overdraft                      |
| `audit.append_log.exchange`               | `audit.append_log.key`                                              | Entradas do log de auditoria (append-log) |

<Note>
  **Esses exchanges vêm desabilitados por padrão.** O operador que executa o Midaz precisa habilitar cada exchange antes que ele emita eventos.
</Note>

### Para operadores self-hosted

Cada exchange é controlado por uma variável de ambiente dedicada, todas com valor padrão `false`:

| Exchange                                  | Variável de ambiente                  |
| ----------------------------------------- | ------------------------------------- |
| `transaction.transaction_events.exchange` | `RABBITMQ_TRANSACTION_EVENTS_ENABLED` |
| `transaction.overdraft_events.exchange`   | `RABBITMQ_OVERDRAFT_EVENTS_ENABLED`   |
| `audit.append_log.exchange`               | `AUDIT_LOG_ENABLED`                   |

Defina a variável como `true` na configuração do seu deployment do Midaz para ativar o exchange correspondente.

### Formato da mensagem

Os eventos de transação envolvem o objeto de domínio em um envelope:

```json theme={null}
{
  "source": "midaz",
  "eventType": "transaction",
  "action": "APPROVED",
  "timeStamp": "2026-07-06T12:00:00Z",
  "version": "<midaz build version>",
  "organizationId": "...",
  "ledgerId": "...",
  "payload": { "...full transaction JSON..." }
}
```

A routing key codifica a ação (`midaz.transaction.APPROVED`, `midaz.balance.overdraft.drawn`), então você pode vincular de forma restrita em vez de filtrar no código.

### Exemplo de configuração

Os parâmetros de conexão vêm da configuração do RabbitMQ do deployment do Midaz (`RABBITMQ_HOST`, `RABBITMQ_PORT_AMQP`, um usuário consumidor como `RABBITMQ_CONSUMER_USER`, o `RABBITMQ_VHOST` por tenant e TLS opcional via `RABBITMQ_TLS`).

```python theme={null}
import pika

params = pika.URLParameters("amqps://consumer:***@midaz-rabbitmq:5671/<tenant-vhost>")
conn = pika.BlockingConnection(params)
ch = conn.channel()

# O exchange é declarado pelo Midaz; declare de forma passiva ou espelhe sua topologia.
exchange = "transaction.transaction_events.exchange"

# Vincule uma fila durável própria às routing keys que te interessam.
ch.queue_declare(queue="my-consumer.transactions", durable=True)
ch.queue_bind(
    queue="my-consumer.transactions",
    exchange=exchange,
    routing_key="midaz.transaction.*",   # todos os status
)

def on_message(chan, method, props, body):
    handle(body)                          # application/json
    chan.basic_ack(method.delivery_tag)   # ack nativo, por mensagem

ch.basic_qos(prefetch_count=10)
ch.basic_consume(queue="my-consumer.transactions", on_message_callback=on_message)
ch.start_consuming()
```

### Quando usar

* Você **opera ou co-loca** o deployment do Midaz e já é dono do broker.
* Você quer **semântica AMQP nativa** — `ack`/`nack` por mensagem, prefetch/QoS, dead-letter exchanges que você controla, consumidores concorrentes em uma mesma fila.
* Seu stack já é nativo de RabbitMQ e você quer o salto de menor latência, dentro do cluster.

### Trade-offs

* Acopla você à topologia e às credenciais do broker interno do Midaz.
* Os exchanges ficam **desabilitados por padrão** e precisam ser habilitados pelo operador.
* Sem retry/dead-letter/auto-desativação gerenciados — a confiabilidade de entrega é sua responsabilidade no lado do consumidor.
* Não é viável para um terceiro que só tem acesso de rede a um Midaz hospedado.

## Opção B — Streaming Hub

***

### Como funciona

O Streaming Hub é o edge de entrega com fan-out da Lerian. Ele consome eventos do barramento de eventos interno do Midaz e entrega cada evento correspondente a um **sink de assinante por tenant** que você registra. Você nunca toca no Kafka nem no broker — você registra uma assinatura por meio de um plano de controle REST e escolhe como quer receber os eventos.

Tipos de sink suportados:

* **`webhook`** — o hub faz POST de cada evento no seu endpoint HTTPS, **assinado com HMAC** com um segredo de assinatura por assinatura.
* **`pull`** — você faz polling em `GET /v1/events`; a própria leitura é o reconhecimento (cursor-as-ack). Nenhum endpoint de entrada é necessário.
* **`sqs`**, **`rabbitmq`**, **`eventbridge`** — o hub entrega na *sua* fila da AWS, no *seu* broker do RabbitMQ ou no *seu* barramento do EventBridge.

O hub adiciona confiabilidade de entrega sobre o fluxo bruto: um inbox durável com deduplicação de consumo único, retry por sink com backoff, dead-lettering ao esgotar e auto-desativação de endpoints que falham de forma persistente.

### Eventos disponíveis

Consulte o catálogo para ver os tipos de evento que você pode assinar:

```http theme={null}
GET /v1/catalog
Authorization: Bearer <jwt>
```

```json theme={null}
{
  "events": [
    {
      "eventType": "transaction.created",
      "topic": "lerian.streaming.transaction.created",
      "schemaVersion": "1.0.0",
      "schemaMajor": 1,
      "description": "A transaction was created."
    }
  ]
}
```

O Midaz publica um catálogo amplo com chaves `<resource>.<event>` (todos no schema `1.0.0`), incluindo:

* `organization.*`, `ledger.*`, `account.*`, `asset.*`, `portfolio.*`, `segment.*` (`created` / `updated` / `deleted`)
* `operation-route.*`, `transaction-route.*` (`created` / `updated` / `deleted`)
* `balance.created`, `balance.config-changed`, `balance.deleted`
* `balance.overdraft-drawn`, `balance.overdraft-repaid`, `balance.overdraft-cleared`
* `transaction.posted`, `transaction.committed`, `transaction.canceled`, `transaction.reverted`

### Autenticação

O plano de controle é **100% JWT do `lib-auth` (Bearer)** — o hub não emite credenciais próprias. Apresente um JWT emitido pelo plugin-auth em cada chamada a `/v1`:

```http theme={null}
Authorization: Bearer <plugin-auth JWT>
```

O tenant é derivado apenas dos claims validados do JWT (`tenantId`, com fallback para `owner`), nunca do corpo da requisição. Um cliente de máquina obtém seu token no fluxo de **client-credentials** do plugin-auth (um id/segredo de aplicação trocado por um token de acesso de curta duração).

### Exemplo de configuração — assinatura webhook

```http theme={null}
POST /v1/subscriptions
Authorization: Bearer <jwt>
X-Idempotency: <unique-key>
Content-Type: application/json

{
  "name": "my-webhook",
  "sink_kind": "webhook",
  "endpoint": "https://hooks.example.com/lerian",
  "event_types": ["transaction.posted", "transaction.committed"],
  "schema_major": 1,
  "plan_tier": "standard"
}
```

`201 Created` retorna a assinatura **e o segredo de assinatura uma única vez** — guarde-o imediatamente, ele só pode ser rotacionado, nunca lido novamente:

```json theme={null}
{
  "subscription": { "id": "0190b8e2-...", "verification_state": "active", "...": "..." },
  "signingSecret": "whsec_...ONCE..."
}
```

Verifique a assinatura HMAC em cada webhook de entrada com esse segredo antes de confiar no payload. Use `POST /v1/subscriptions/:id/ping` para enviar um evento sintético assinado e confirmar que seu endpoint está corretamente conectado.

### Exemplo de configuração — assinatura pull (adequada para serverless)

Crie-a com `"sink_kind": "pull"` (omita `endpoint` — o servidor sintetiza um) e então faça polling:

```http theme={null}
GET /v1/events?subscription_id=<uuid>&limit=100
Authorization: Bearer <jwt>
```

```json theme={null}
{
  "events": [
    { "seq": 42, "ceId": "...", "eventType": "transaction.posted",
      "receivedAt": "2026-07-06T12:00:00Z", "payload": { "...": "..." } }
  ],
  "next_cursor": 42
}
```

A leitura é o ack: o maior `seq` retornado avança um cursor durável e monotônico. Reenvie o `next_cursor` emitido pelo servidor como `?after=` na próxima chamada. Cada evento carrega `ceId` para sua própria deduplicação.

### Sinks de fila (SQS / RabbitMQ / EventBridge)

Assinaturas do tipo fila são criadas **sem** credencial e nascem em `pending_verification` — não emitem nada até você fornecer uma credencial de saída via `PUT /v1/subscriptions/:id/credential`, que o hub testa (conexão + autenticação) e, em caso de sucesso, muda a assinatura para `active`. Para um sink do RabbitMQ, o `endpoint` é `"<exchange>/<routingKey>"` e o host do broker fica na credencial criptografada; para sinks da AWS, obtenha a policy de trust do IAM + `ExternalId` em `GET /v1/subscriptions/:id/setup-artifacts` e configure a concessão entre contas antes do PUT da credencial.

### Quando usar

* Você é um **integrador externo** com apenas acesso de rede a um Midaz hospedado.
* Você quer entrega **serverless** — um endpoint webhook ou um loop de pull HTTP, sem broker para operar.
* Você quer **confiabilidade gerenciada** — deduplicação, retry/backoff, dead-lettering, auto-desativação, webhooks assinados — sem construir isso você mesmo.
* Você quer distribuir os mesmos eventos para infraestrutura **nativa da AWS** (SQS/EventBridge).

### Limitações

* Sem transporte SSE ou WebSocket — a entrega é push por webhook ou pull HTTP (além dos sinks de fila).
* O catálogo é **global** (idêntico independentemente de qual tenant se autentica).
* Consumidores pull são donos da posição do seu cursor — um avanço além do cursor pula esse intervalo permanentemente. Use `?after=` para reprocessar a partir de qualquer `seq` anterior.

***

## Tabela de decisão

| Critério                      | RabbitMQ direto                                                        | Streaming Hub                                                            |
| ----------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| **Seu stack**                 | Já é nativo de RabbitMQ / AMQP, dentro do cluster                      | Qualquer coisa que fale HTTPS (ou AWS SQS/EventBridge)                   |
| **Modelo de deployment**      | Você opera / co-loca o Midaz e é dono do broker                        | Midaz hospedado / SaaS, integrador externo                               |
| **Ack granular**              | ✅ `ack`/`nack` nativo por mensagem, prefetch, DLX                      | Parcial — 2xx do webhook ou cursor-as-ack do pull; sem nack por mensagem |
| **Serverless**                | ❌ Requer um consumidor AMQP de longa duração                           | ✅ Endpoint webhook ou loop de pull sem estado                            |
| **Simplicidade para começar** | ❌ Creds do broker, vhost, exchange que precisa ser habilitado          | ✅ Um `POST /v1/subscriptions` autenticado                                |
| **Confiabilidade gerenciada** | ❌ Retry / DLQ / backoff por sua conta                                  | ✅ Deduplicação, retry/backoff, dead-letter, auto-desativação             |
| **Latência**                  | A mais baixa (salto dentro do cluster)                                 | Ligeiramente maior (edge de fan-out)                                     |
| **Modelo de auth**            | Usuário/senha do broker (+ TLS opcional)                               | JWT do plugin-auth (Bearer); webhooks assinados                          |
| **Escopo de eventos**         | Exchanges de transação / overdraft / auditoria (devem ser habilitados) | Catálogo completo do Midaz via `/v1/catalog`                             |
| **Distribuir para AWS**       | ❌ Construa você mesmo                                                  | ✅ Sinks de SQS / EventBridge                                             |

**Regra prática:** se você é dono do broker e quer controle AMQP bruto, use **RabbitMQ direto**. Em qualquer outro caso — integração externa, serverless, confiabilidade gerenciada, entrega para AWS — use o Streaming Hub.

***

## Considerações de segurança

* **Credenciais de privilégio mínimo.** Para RabbitMQ direto, conecte-se com um usuário **com escopo de consumidor** (não o usuário publicador/padrão), restrito ao vhost do tenant, e habilite TLS (`amqps://`). Para o Streaming Hub, limite a aplicação do plugin-auth ao tenant que ela representa e rotacione o segredo de cliente.
* **Verifique as assinaturas dos webhooks.** Sempre valide a assinatura HMAC-v1 com seu segredo de assinatura por assinatura antes de agir sobre um webhook. Trate uma requisição sem assinatura ou com assinatura divergente como hostil.
* **Proteja o segredo de assinatura.** Ele é exibido uma única vez na criação/rotação e depois não pode ser recuperado. Guarde-o em um gerenciador de segredos; se vazar, rotacione via `POST /v1/subscriptions/:id/secret/rotate` (sobreposição de dupla assinatura de 24 h).
* **Endpoints somente HTTPS.** Sinks de webhook devem ser `https://`. O hub valida os endpoints contra SSRF na criação e no PUT da credencial; destinos em texto plano/privados são rejeitados.
* **O isolamento de tenant é derivado dos claims.** O hub lê a identidade do tenant apenas dos claims validados do JWT (ou `ce-tenantid` na ingestão), nunca do corpo da requisição — não tente passar um `tenant_id` nos payloads.
* **Idempotência e deduplicação.** Envie `X-Idempotency` nas chamadas mutantes do plano de controle. No plano de dados, deduplique por `ceId` (Streaming Hub) ou pelo id da mensagem / sua própria chave (RabbitMQ), já que ambos os transportes são pelo-menos-uma-vez.
* **Não exponha as entranhas do Midaz.** O exchange interno de operações de saldo e as credenciais do broker nunca devem ser compartilhados com consumidores externos; coloque terceiros atrás do Streaming Hub.
