Pular para o conteúdo principal
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:
EventoRabbitMQ diretoStreaming Hub
TransporteAMQP 0-9-1Webhook / Pull (HTTP) / SQS / RabbitMQ / EventBridge
Você se conecta aO broker do MidazUma API REST do plano de controle
AcoplamentoForte (infra do Midaz)Fraco (edge gerenciado)
Ideal paraDeployments self-hosted / co-locadosIntegradores externos / SaaS
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.

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:
ExchangePadrão de routing keyEmite
transaction.transaction_events.exchangemidaz.transaction.<STATUS> (ex.: midaz.transaction.APPROVED)Envelopes do ciclo de vida de transações
transaction.overdraft_events.exchangemidaz.balance.overdraft.<action> (drawn / repaid / cleared)Eventos de overdraft
audit.append_log.exchangeaudit.append_log.keyEntradas do log de auditoria (append-log)
Esses exchanges vêm desabilitados por padrão. O operador que executa o Midaz precisa habilitar cada exchange antes que ele emita eventos.

Para operadores self-hosted

Cada exchange é controlado por uma variável de ambiente dedicada, todas com valor padrão false:
ExchangeVariável de ambiente
transaction.transaction_events.exchangeRABBITMQ_TRANSACTION_EVENTS_ENABLED
transaction.overdraft_events.exchangeRABBITMQ_OVERDRAFT_EVENTS_ENABLED
audit.append_log.exchangeAUDIT_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:
{
  "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).
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 nativaack/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:
GET /v1/catalog
Authorization: Bearer <jwt>
{
  "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:
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

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:
{
  "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:
GET /v1/events?subscription_id=<uuid>&limit=100
Authorization: Bearer <jwt>
{
  "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érioRabbitMQ diretoStreaming Hub
Seu stackJá é nativo de RabbitMQ / AMQP, dentro do clusterQualquer coisa que fale HTTPS (ou AWS SQS/EventBridge)
Modelo de deploymentVocê opera / co-loca o Midaz e é dono do brokerMidaz hospedado / SaaS, integrador externo
Ack granularack/nack nativo por mensagem, prefetch, DLXParcial — 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ênciaA mais baixa (salto dentro do cluster)Ligeiramente maior (edge de fan-out)
Modelo de authUsuário/senha do broker (+ TLS opcional)JWT do plugin-auth (Bearer); webhooks assinados
Escopo de eventosExchanges 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.