> ## 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.

# Webhooks

> How to configure and handle webhooks for Pix Indirect operations via BTG.

Webhooks are the primary mechanism the **Pix Indirect Plugin (BTG)** uses to notify you about Pix-related events in real time.

Instead of relying on synchronous responses, you receive **asynchronous, event-driven callbacks** whenever relevant changes occur in Pix operations — such as transfers, refunds, key claims, or MED-related events.

This model ensures:

* Near real-time updates
* Decoupled integrations
* Reliable reconciliation and operational traceability

<Warning>
  Webhooks described on this page currently apply to the **Indirect Pix model via BTG**.

  Direct Pix webhooks may differ depending on the connectivity model and are documented separately.
</Warning>

# Prerequisites

***

Before configuring webhooks, ensure you have:

* The Pix Indirect Plugin configured and running (see [How indirect participation works](/en/midaz/plugins/pix/pix-overview))
* An HTTPS endpoint ready to receive webhook requests
* Basic understanding of Pix event lifecycle and transaction flows

# Why webhooks matter in Pix

***

Pix is an asynchronous, multi-party system.

Even when an API request succeeds, the **final state of a transaction is only confirmed later**, after settlement and counterparty acknowledgment.

Webhooks allow your system to:

* Track **authoritative transaction status**
* React to **refunds, reversals, and MED events**
* Maintain **ledger and operational consistency**
* Reduce polling and operational overhead

# Event types

***

You receive events grouped by **flow** and **entity**, aligned with BACEN (Banco Central do Brasil) domains.

| Flow     | Entity                 | Description                                                                    |
| -------- | ---------------------- | ------------------------------------------------------------------------------ |
| DICT     | CLAIM                  | Pix key portability and ownership claim events                                 |
| DICT     | INFRACTION\_REPORT     | MED (Mecanismo Especial de Devolução) infraction reports and dispute lifecycle |
| DICT     | REFUND                 | MED refund request events                                                      |
| DICT     | FUNDS\_RECOVERY        | MED 2.0 Funds Recovery entity status changes (DB-backed)                       |
| DICT     | FUNDS\_RECOVERY\_EVENT | MED 2.0 Funds Recovery lifecycle events (pass-through)                         |
| TRANSFER | CASHIN                 | Incoming Pix payment status updates                                            |
| TRANSFER | CASHOUT                | Outgoing Pix payment status updates                                            |
| REFUND   | CASHIN                 | Incoming Pix refund status updates                                             |
| REFUND   | CASHOUT                | Outgoing Pix refund status updates                                             |

Each event reflects a **state transition** in the Pix ecosystem and should be treated as the source of truth.

<Note>
  The two **MED 2.0** entities behave differently: `FUNDS_RECOVERY` is emitted after the plugin updates its local record, while `FUNDS_RECOVERY_EVENT` is a pass-through of BTG lifecycle events with no database update. See [MED 2.0 — Funds Recovery](/en/midaz/plugins/pix/indirect/indirect-pix-med-2-funds-recovery) for the full flow.
</Note>

<Note>
  **DICT** (Diretório de Identificadores de Contas Transacionais) is the BACEN directory that manages Pix keys and related operations like claims, infractions, and refunds.
</Note>

# Webhook configuration

***

You enable webhooks by configuring **destination URLs** and selecting which event types your system should receive.

## Environment variables

***

You can configure webhook endpoints at **entity**, **flow**, or **global** level.

| Flow     | Entity             | Entity-level URL variable            |
| -------- | ------------------ | ------------------------------------ |
| DICT     | CLAIM              | `WEBHOOK_DICT_CLAIM_URL`             |
| DICT     | INFRACTION\_REPORT | `WEBHOOK_DICT_INFRACTION_REPORT_URL` |
| DICT     | REFUND             | `WEBHOOK_DICT_REFUND_URL`            |
| TRANSFER | CASHIN             | `WEBHOOK_TRANSFER_CASHIN_URL`        |
| TRANSFER | CASHOUT            | `WEBHOOK_TRANSFER_CASHOUT_URL`       |
| REFUND   | CASHIN             | `WEBHOOK_REFUND_CASHIN_URL`          |
| REFUND   | CASHOUT            | `WEBHOOK_REFUND_CASHOUT_URL`         |

Each flow also has a **flow-level URL** that applies to all of its entities when no entity-level URL is set: `WEBHOOK_DICT_URL`, `WEBHOOK_TRANSFER_URL`, and `WEBHOOK_REFUND_URL`.

## URL resolution priority

***

When multiple URLs are configured, the system resolves them in the following order:

1. **Entity-level URL**

   Example: `WEBHOOK_DICT_CLAIM_URL`

2. **Flow-level URL**

   Example: `WEBHOOK_DICT_URL`

3. **Default URL**

   `WEBHOOK_DEFAULT_URL`

This gives you fine-grained routing control without duplicating infrastructure.

# Request format

***

## Headers

***

Every webhook request includes standardized headers for traceability and security.

| Header            | Description                               |
| ----------------- | ----------------------------------------- |
| `Content-Type`    | `application/json`                        |
| `X-Request-ID`    | Unique request identifier                 |
| `X-Entity-Type`   | Event entity (e.g. `INFRACTION_REPORT`)   |
| `X-Flow-Type`     | Source domain (e.g. `DICT`)               |
| `Idempotency-Key` | Unique event identifier for deduplication |

## Body structure

***

```json theme={null}
{
  "entityType": "INFRACTION_REPORT",
  "flowType": "DICT",
  "payload": {
    ...
  }
}
```

| Field        | Description         |
| ------------ | ------------------- |
| `entityType` | Event entity        |
| `flowType`   | Pix domain          |
| `payload`    | Event-specific data |

The payload schema varies per event type but always represents a **state change**.

# Responses and retry behavior

***

## Expected response

***

Your endpoint must return an **HTTP 2xx** status to confirm successful delivery.

| Response | Result                 |
| -------- | ---------------------- |
| 2xx      | Delivered successfully |
| Non-2xx  | Retried automatically  |

## Retry strategy

***

Failed deliveries are retried automatically using **exponential backoff**:

| Attempt | Delay     |
| ------- | --------- |
| 1       | 1 second  |
| 2       | 2 seconds |
| 3       | 4 seconds |

**Defaults**

* Max retries: 3
* Timeout per request: 30 seconds

After all retries fail, the event is moved to a **failure queue** for operational follow-up.

### Custom retry settings

Retries and timeouts can be customized per event:

```bash theme={null}
WEBHOOK_DICT_INFRACTION_REPORT_MAX_RETRIES=5
WEBHOOK_DICT_INFRACTION_REPORT_REQUEST_TIMEOUT=60s
```

## Circuit breaker protection

***

To prevent cascading failures, webhook delivery is protected by a **circuit breaker**.

When the Pix Plugin detects **repeated delivery failures** (typically consecutive `5xx` responses or timeouts), it temporarily **pauses webhook calls** to the affected endpoint.

After a configurable cooldown period, the system performs **controlled retry attempts** to check if the endpoint has recovered.

Once successful responses are detected, normal delivery is automatically resumed.

This mechanism ensures:

* Protection against overloaded or unstable endpoints
* Graceful recovery without manual intervention
* Higher overall system stability in production environments

<Note>
  Circuit breaker behavior works alongside retries and exponential backoff, adding an extra safety layer for webhook delivery.
</Note>

## Transport errors and orphan events

***

A delivery attempt can fail at the transport layer (connection refused, DNS failure, TLS handshake error) before any HTTP status is returned. These failures are **persisted as orphan events** rather than lost, so they remain visible for operational follow-up and reprocessing instead of silently disappearing.

When handling refund events, note that `originalEndToEndId` is the canonical key for all refund lookups. The plugin now resolves refunds from both the cash-in → refund and cash-out → refund directions using this field, so always key refunds by `originalEndToEndId` (the original transfer's end-to-end ID) rather than assuming a single, direction-specific lookup path.

# Internal transaction reports (intra-PSP)

***

Intra-PSP (P2P) transfers are settled internally and never reach BTG for settlement, but they are still reported to BACEN via the **TRCK002** abstraction. BTG confirms report status through a **CAMT025** webhook carrying the entity `PixInternalTransactionsReport`.

| Field                            | Description                                         |
| -------------------------------- | --------------------------------------------------- |
| `pactualId`                      | BTG-assigned report identifier                      |
| `clientRequestId`                | Your idempotency key, sent during report submission |
| `entity`                         | Always `PixInternalTransactionsReport`              |
| `status`                         | `PROCESSING`, `CONFIRMED`, or `ERROR`               |
| `errorCode` / `errorDescription` | Populated when `status = ERROR`                     |

The plugin updates the report status and, on completion or failure, emits the standard intra-PSP outbound events (`cashout.completed`/`cashout.failed`, `cashin.completed`, and the refund equivalents). For the full internal flow, see [Intra-PSP transfers](/en/midaz/plugins/pix/indirect/indirect-pix-intra-psp).

# Best practices

***

| Practice                  | Why it matters                                                                                    |
| ------------------------- | ------------------------------------------------------------------------------------------------- |
| **Ignore unknown fields** | Remain forward-compatible as new fields are added                                                 |
| **Idempotent processing** | Use `Idempotency-Key` to avoid processing duplicates                                              |
| **Fast acknowledgment**   | Return `202 Accepted` and process asynchronously                                                  |
| **Async processing**      | Avoid blocking the webhook thread                                                                 |
| **Handle compression**    | Payloads >1KB are gzip-compressed. Check the `Content-Encoding` header and decompress accordingly |

# Event examples

***

Below are representative examples of webhook payloads you receive from the Pix Indirect Plugin. Expand each entry to view its payload.

<AccordionGroup>
  <Accordion title="DICT claim">
    Ownership or portability lifecycle events. Use these to track Pix key disputes across institutions.

    ```json theme={null}
    {
      "entityType": "CLAIM",
      "flowType": "DICT",
      "payload": {
        "id": "claim-7f8a9b2c-1234-5678-abcd-ef0123456789",
        "key": "+5511999998888",
        "keyType": "PHONE",
        "claimType": "PORTABILITY",
        "claimer": {
          "ispb": "12345678",
          "name": "Banco Exemplo S.A."
        },
        "donor": {
          "ispb": "87654321",
          "name": "Outra Instituição S.A."
        },
        "status": "CONFIRMED",
        "createdAt": "2024-01-15T10:30:00Z",
        "updatedAt": "2024-01-15T14:45:00Z"
      }
    }
    ```
  </Accordion>

  <Accordion title="DICT infraction report (MED)">
    Dispute and fraud signaling events aligned with BACEN MED rules.

    ```json theme={null}
    {
      "entityType": "INFRACTION_REPORT",
      "flowType": "DICT",
      "payload": {
        "id": "infraction-3e4f5a6b-7890-1234-cdef-567890abcdef",
        "endToEndId": "E12345678202401151030abcdefghij12",
        "infractionType": "FRAUD",
        "reportedBy": {
          "ispb": "12345678",
          "name": "Banco Exemplo S.A."
        },
        "reportedAgainst": {
          "ispb": "87654321",
          "name": "Outra Instituição S.A."
        },
        "status": "OPEN",
        "analysisResult": null,
        "createdAt": "2024-01-15T10:30:00Z",
        "updatedAt": "2024-01-15T10:30:00Z"
      }
    }
    ```
  </Accordion>

  <Accordion title="DICT refund (MED)">
    Refund requests and decisions related to MED cases.

    ```json theme={null}
    {
      "entityType": "REFUND",
      "flowType": "DICT",
      "payload": {
        "id": "refund-9a8b7c6d-5432-1098-fedc-ba0987654321",
        "endToEndId": "E12345678202401151030abcdefghij12",
        "infractionId": "infraction-3e4f5a6b-7890-1234-cdef-567890abcdef",
        "refundAmount": 150.00,
        "refundReason": "FRAUD",
        "status": "REQUESTED",
        "requestedBy": {
          "ispb": "12345678",
          "name": "Banco Exemplo S.A."
        },
        "createdAt": "2024-01-16T09:00:00Z",
        "updatedAt": "2024-01-16T09:00:00Z"
      }
    }
    ```
  </Accordion>

  <Accordion title="DICT funds recovery (MED 2.0)">
    Funds Recovery entity status changes. The plugin updates its local record before forwarding the full entity.

    ```json theme={null}
    {
      "entityType": "FUNDS_RECOVERY",
      "flowType": "DICT",
      "payload": {
        "id": "91d65e98-97c0-4b0f-b577-73625da1f9fc",
        "externalId": "ca1b9c01-ff9e-4a58-90ab-d31512e15ce0",
        "accountId": "01989f9e-6508-79f8-9540-835be49fbd0d",
        "status": "CREATED",
        "rootTransactionId": "E9999901012341234123412345678900",
        "situationType": "SCAM",
        "reporterParticipant": "99999010",
        "contactInformation": {},
        "reportDetails": "Details to help receiving participants",
        "createdAt": "2020-01-17T10:00:00.000Z",
        "updatedAt": "2020-01-17T10:00:00.000Z"
      }
    }
    ```

    Lifecycle events arrive as `entityType: FUNDS_RECOVERY_EVENT` (pass-through, no DB update), with `event` values such as `FUNDS_RECOVERY_ANALYSED` and `FUNDS_RECOVERY_COMPLETED`.
  </Accordion>

  <Accordion title="Transfer cash-in and cash-out">
    Incoming and outgoing Pix transfer events.

    **Cash-in (incoming transfer):**

    ```json theme={null}
    {
      "entityType": "CASHIN",
      "flowType": "TRANSFER",
      "payload": {
        "id": "transfer-1a2b3c4d-5678-90ab-cdef-1234567890ab",
        "endToEndId": "E12345678202401151030abcdefghij12",
        "amount": 250.00,
        "payer": {
          "ispb": "87654321",
          "name": "João Silva",
          "cpfCnpj": "12345678901"
        },
        "payee": {
          "ispb": "12345678",
          "name": "Maria Santos",
          "cpfCnpj": "98765432100",
          "accountNumber": "12345-6"
        },
        "status": "SETTLED",
        "createdAt": "2024-01-15T10:30:00Z",
        "settledAt": "2024-01-15T10:30:05Z"
      }
    }
    ```

    **Cash-out (outgoing transfer):**

    ```json theme={null}
    {
      "entityType": "CASHOUT",
      "flowType": "TRANSFER",
      "payload": {
        "id": "transfer-2b3c4d5e-6789-01bc-def0-2345678901bc",
        "endToEndId": "E87654321202401151045zyxwvutsrqp98",
        "amount": 500.00,
        "payer": {
          "ispb": "12345678",
          "name": "Maria Santos",
          "cpfCnpj": "98765432100",
          "accountNumber": "12345-6"
        },
        "payee": {
          "ispb": "87654321",
          "name": "Empresa ABC Ltda",
          "cpfCnpj": "12345678000199"
        },
        "status": "SETTLED",
        "createdAt": "2024-01-15T10:45:00Z",
        "settledAt": "2024-01-15T10:45:03Z"
      }
    }
    ```
  </Accordion>

  <Accordion title="Refund cash-in and cash-out">
    Refund settlement events for Pix transactions.

    **Refund cash-in (receiving a refund):**

    ```json theme={null}
    {
      "entityType": "CASHIN",
      "flowType": "REFUND",
      "payload": {
        "id": "refund-4d5e6f7g-8901-23cd-ef01-4567890123cd",
        "originalEndToEndId": "E87654321202401151045zyxwvutsrqp98",
        "refundEndToEndId": "D12345678202401161000refund123456",
        "amount": 500.00,
        "reason": "CUSTOMER_REQUEST",
        "status": "SETTLED",
        "createdAt": "2024-01-16T10:00:00Z",
        "settledAt": "2024-01-16T10:00:02Z"
      }
    }
    ```

    **Refund cash-out (sending a refund):**

    ```json theme={null}
    {
      "entityType": "CASHOUT",
      "flowType": "REFUND",
      "payload": {
        "id": "refund-5e6f7g8h-9012-34de-f012-5678901234de",
        "originalEndToEndId": "E12345678202401151030abcdefghij12",
        "refundEndToEndId": "D87654321202401161015refund789012",
        "amount": 250.00,
        "reason": "OPERATIONAL_FLAW",
        "status": "SETTLED",
        "createdAt": "2024-01-16T10:15:00Z",
        "settledAt": "2024-01-16T10:15:04Z"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

# Key takeaway

***

Webhooks are **not optional** in Pix Indirect operations.

They are the **authoritative channel** for transaction state, refunds, and dispute handling.

A correct webhook implementation ensures:

* Accurate reconciliation
* Regulatory compliance
* Operational resilience
* Predictable customer experience

For production environments, always design webhook consumers as **idempotent, asynchronous, and observable systems**.

# Next steps

***

Now that you understand how webhooks work in Indirect Pix, explore these related topics:

* [Pix main domains: transfers](/en/midaz/plugins/pix/main-domains-transactions) - Deep dive into transfer operations
* [Pix main domains: DICT](/en/midaz/plugins/pix/main-domains-dict) - Understanding DICT operations and key management
* [Pix main domains: MED](/en/midaz/plugins/pix/main-domains-med) - MED dispute and refund handling
* [MED 2.0 — Funds Recovery](/en/midaz/plugins/pix/indirect/indirect-pix-med-2-funds-recovery) - Cross-account fraud recovery and its webhooks
* [Intra-PSP transfers](/en/midaz/plugins/pix/indirect/indirect-pix-intra-psp) - Internal P2P settlement and TRCK002 reporting
* [API reference](/en/reference/midaz/plugins/indirect-pix/create-entry) — Full API documentation for DICT, Claims, Transactions, QR Codes, and MED operations
