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

# Payment events

> Catalog of webhook events emitted for the payment-order lifecycle.

BlooBank emits webhook events on every meaningful transition in the lifecycle of a **Payment Order**. All events follow the standard envelope described in [Webhooks overview](/get-started/webhooks/overview); this page is the catalog plus sample payloads.

## Event catalog

| Event                      | Direction    | Fires when                                                                     | Triggered by |
| -------------------------- | ------------ | ------------------------------------------------------------------------------ | ------------ |
| `payment_order.created`    | `IN` / `OUT` | Order created via `POST .../paymentOrders`.                                    | Caller       |
| `payment_order.approved`   | `OUT`        | `PUT .../approve` succeeds. Order transitions `AWAITING_APPROVAL` → `PENDING`. | Caller       |
| `payment_order.canceled`   | `OUT`        | `PUT .../cancel` succeeds. Order transitions `AWAITING_APPROVAL` → `CANCELED`. | Caller       |
| `payment_order.processing` | `IN` / `OUT` | Provider acknowledges and starts executing the order.                          | Platform     |
| `payment_order.success`    | `IN` / `OUT` | Provider confirms settlement. Order is `SUCCESS`.                              | Platform     |
| `payment_order.failed`     | `IN` / `OUT` | Provider rejected or could not complete. Order is `FAILED`.                    | Platform     |
| `payment_order.expired`    | `IN`         | Inbound dynamic QR/EMV `expiresIn` elapsed without settlement.                 | Platform     |
| `payment_order.refunded`   | `IN` / `OUT` | A previously `SUCCESS` order was refunded.                                     | Platform     |

Events follow the `{resource}.{transition}` naming convention. The `payment_order.*` namespace is reserved for orders managed by the Transactions Engine.

## The envelope

Every event has the same outer shape:

```json theme={"dark"}
{
  "event":            "payment_order.success",
  "messageId":        "msg_8H2kPqR9zT4xVm",
  "occurredAt":       "2026-01-15T10:31:24.512Z",
  "deliveryAttempt":  1,
  "data": { /* the full PaymentOrder resource */ }
}
```

| Field             | Description                                                                                                |
| ----------------- | ---------------------------------------------------------------------------------------------------------- |
| `event`           | The event name.                                                                                            |
| `messageId`       | Unique delivery id — use to deduplicate.                                                                   |
| `occurredAt`      | When the underlying transition happened (UTC ISO 8601).                                                    |
| `deliveryAttempt` | 1-based retry counter.                                                                                     |
| `data`            | The full `PaymentOrder` resource at the post-transition state. Same shape as `GET .../paymentOrders/{id}`. |

## Sample payloads

### `payment_order.created` — inbound

```json theme={"dark"}
{
  "event":      "payment_order.created",
  "messageId":  "msg_5G9kLnM2yV3wXz",
  "occurredAt": "2026-01-15T10:30:00.000Z",
  "deliveryAttempt": 1,
  "data": {
    "id":              "ord_3KpFvBwYzNqMxA7eHbRdJ",
    "kind":            "Payment.Order",
    "wallet":          "production-main",
    "ordVersion":      1,
    "direction":       "IN",
    "status":          "PENDING",
    "network":         "br.gov.bcb.pix",
    "idempotencyKey":  "invoice-2026-0184",
    "amount":          25000,
    "currency":        "BRL",
    "instrument": {
      "type":      "PIX_CASH_IN_EMV_DYNAMIC",
      "expiresIn": 86400,
      "qrcode":    "00020101021126830014br.gov.bcb.pix...",
      "copypaste": "00020101021126830014br.gov.bcb.pix...",
      "expiresAt": "2026-01-16T10:30:00.000Z",
      "endToEndId": null
    },
    "metadata":    { "orderId": "2026-0184" },
    "createdAt":   "2026-01-15T10:30:00.000Z",
    "updatedAt":   "2026-01-15T10:30:00.000Z",
    "processedAt": null
  }
}
```

### `payment_order.success` — inbound

Same `data.id`; later state. The instrument now has `endToEndId` populated and `processedAt` is set.

```json theme={"dark"}
{
  "event":      "payment_order.success",
  "messageId":  "msg_8H2kPqR9zT4xVm",
  "occurredAt": "2026-01-15T10:31:24.512Z",
  "deliveryAttempt": 1,
  "data": {
    "id":              "ord_3KpFvBwYzNqMxA7eHbRdJ",
    "ordVersion":      3,
    "direction":       "IN",
    "status":          "SUCCESS",
    "instrument": {
      "type":       "PIX_CASH_IN_EMV_DYNAMIC",
      "endToEndId": "E60746948202601151030001A1B2C3D"
    },
    "processedAt": "2026-01-15T10:31:24.512Z"
  }
}
```

(Other fields omitted for brevity — every event carries the full resource shape.)

### `payment_order.created` — outbound

```json theme={"dark"}
{
  "event":      "payment_order.created",
  "messageId":  "msg_6H0lMoN3zW4xYa",
  "occurredAt": "2026-01-15T11:00:00.000Z",
  "deliveryAttempt": 1,
  "data": {
    "id":              "ord_4LqGwCxZzOrNyB8fHcSeK",
    "direction":       "OUT",
    "status":          "AWAITING_APPROVAL",
    "amount":          15000,
    "currency":        "BRL",
    "idempotencyKey":  "payout-2026-0309",
    "instrument": {
      "type":     "PIX_CASH_OUT_KEY",
      "keyType":  "CPF",
      "keyValue": "12345678901"
    }
  }
}
```

### `payment_order.approved` — outbound

```json theme={"dark"}
{
  "event":      "payment_order.approved",
  "messageId":  "msg_7H1mNpO4aX5yZb",
  "occurredAt": "2026-01-15T11:00:01.000Z",
  "deliveryAttempt": 1,
  "data": {
    "id":     "ord_4LqGwCxZzOrNyB8fHcSeK",
    "status": "PENDING"
  }
}
```

### `payment_order.success` — outbound

```json theme={"dark"}
{
  "event":      "payment_order.success",
  "messageId":  "msg_9I3lQrS0aU5yWn",
  "occurredAt": "2026-01-15T11:00:03.214Z",
  "deliveryAttempt": 1,
  "data": {
    "id":     "ord_4LqGwCxZzOrNyB8fHcSeK",
    "status": "SUCCESS",
    "instrument": {
      "type":       "PIX_CASH_OUT_KEY",
      "keyType":    "CPF",
      "keyValue":   "12345678901",
      "endToEndId": "E60746948202601151100001B2C3D4E",
      "creditorAccount": {
        "ispb":                 "60746948",
        "bankCode":             "237",
        "bankName":             "Banco Bradesco S.A.",
        "agencyNumber":         "1234",
        "accountType":          "CHECKING_ACCOUNT",
        "accountNumber":        "987654-3",
        "accountOwnerName":     "Recipient Name",
        "accountOwnerDocument": "12345678901"
      }
    },
    "processedAt": "2026-01-15T11:00:03.214Z"
  }
}
```

### `payment_order.failed` — outbound

```json theme={"dark"}
{
  "event":      "payment_order.failed",
  "messageId":  "msg_3F8jKlN1xC7Bz",
  "occurredAt": "2026-01-15T11:00:02.314Z",
  "deliveryAttempt": 1,
  "data": {
    "id":           "ord_5MrHxDyAaPsOzC9gIdTfL",
    "direction":    "OUT",
    "status":       "FAILED",
    "errorCode":    "INSUFFICIENT_FUNDS",
    "errorMessage": "Insufficient available balance to complete this transfer.",
    "processedAt":  "2026-01-15T11:00:02.314Z"
  }
}
```

`errorCode` is a stable `UPPER_SNAKE_CASE` value. Common codes include `INSUFFICIENT_FUNDS`, `RECIPIENT_INVALID`, `LIMIT_EXCEEDED`, `NETWORK_REJECTED`.

### `payment_order.expired` — inbound only

```json theme={"dark"}
{
  "event":      "payment_order.expired",
  "messageId":  "msg_4G9kLmO2yD8Ca",
  "occurredAt": "2026-01-16T10:30:00.000Z",
  "deliveryAttempt": 1,
  "data": {
    "id":     "ord_6NsIyEzBbQtPaD0hJeUgM",
    "status": "EXPIRED"
  }
}
```

## Routing

A reasonable handler shape:

```ts theme={"dark"}
async function handleWebhook(event) {
  switch (event.event) {
    case 'payment_order.created':
      return markCreated(event.data);
    case 'payment_order.approved':
      return markApproved(event.data);
    case 'payment_order.processing':
      return markProcessing(event.data);
    case 'payment_order.success':
      return reconcileSuccess(event.data);
    case 'payment_order.failed':
      return reconcileFailed(event.data);
    case 'payment_order.canceled':
    case 'payment_order.expired':
      return markTerminal(event.data);
    case 'payment_order.refunded':
      return processRefund(event.data);
    default:
      console.warn(`Unknown event: ${event.event}`);
      // accept the delivery; ignore the event
      return;
  }
}
```

For unknown event names — the catalog evolves additively — accept the delivery (return 2xx) and log a warning. Never `4xx` an unknown event; that marks the delivery permanently failed.

## Direction filtering

If you only care about inbound or outbound:

```ts theme={"dark"}
if (event.data.direction !== 'IN') return;
```

The `data.direction` field is always present on `payment_order.*` events.

## Ordering caveats

Events on the same order can arrive out of order. For example, you may receive `success` before `processing` if the underlying state machine advanced quickly and the `processing` delivery was retried after a transient failure on your side.

**Use `data.status` as the source of truth.** If a `processing` event arrives after you have already seen `success`, the order is still `SUCCESS` — the late event does not regress your local state.

A robust handler updates local state using a monotonic version (`data.ordVersion`), refusing to roll back to an older version:

```ts theme={"dark"}
if (event.data.ordVersion <= localOrder.lastSeenVersion) {
  // ignore — we have already seen a fresher snapshot
  return;
}
localOrder.applyEvent(event);
```

## Next

<CardGroup cols={3}>
  <Card title="Webhooks overview" icon="webhook" href="/get-started/webhooks/overview">
    Delivery contract.
  </Card>

  <Card title="Verifying signatures" icon="signature" href="/get-started/webhooks/signature-verification">
    Verify before processing.
  </Card>

  <Card title="Best practices" icon="check-double" href="/get-started/webhooks/best-practices">
    Idempotent consumer pattern.
  </Card>
</CardGroup>
