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

# Webhook events catalog

> Reference for the operation_created, operation_updated, and operation_error events emitted by the request-money API, including the data payload for each.

Every webhook delivery uses the [common envelope](/request-api/webhooks/overview#payload-envelope):

```json theme={null}
{ "event": "<event_name>", "data": { "...": "..." }, "timestamp": "2026-05-06T10:00:00.000Z" }
```

This page documents the `data` object for each event.

## `operation_created`

Emitted after a successful **`POST /v1/request-money`** persists a remittance for your partner. The `data` object is a snapshot of the operation as you would receive it through the public API plus the inbound `requestData`.

<ResponseField name="partnerId" type="string" required>
  Stable identifier of the partner that owns the operation.
</ResponseField>

<ResponseField name="referenceId" type="string" required>
  Eight-character alphanumeric operation code you supplied on `POST`.
</ResponseField>

<ResponseField name="id" type="string" required>
  Operation identifier. Currently equal to `referenceId`.
</ResponseField>

<ResponseField name="userReferenceId" type="string" required>
  End-user identifier you supplied inside `requestData`.
</ResponseField>

<ResponseField name="amount" type="number" required>
  Amount of the operation, copied from the inbound payload.
</ResponseField>

<ResponseField name="currency" type="string" required>
  ISO 4217 currency code copied from the inbound payload.
</ResponseField>

<ResponseField name="expiresInMinutes" type="number" required>
  Validity window of the operation in minutes.
</ResponseField>

<ResponseField name="status" type="string" required>
  One of `pending`, `processing`, `completed`, `rejected`, `cancelled`. New operations always emit with `pending`.
</ResponseField>

<ResponseField name="statusDetails" type="string">
  Human-readable description of `status`.
</ResponseField>

<ResponseField name="waLink" type="string" required>
  Absolute WhatsApp link generated for the payer.
</ResponseField>

<ResponseField name="landingLink" type="string" required>
  Absolute landing-page link generated for the payer.
</ResponseField>

<ResponseField name="requestData" type="object" required>
  The full `requestData` you submitted (applicant, address, payout method).
</ResponseField>

<ResponseField name="createdAt" type="string">
  ISO 8601 instant the operation was first stored.
</ResponseField>

<ResponseField name="updatedAt" type="string">
  ISO 8601 instant of the most recent persisted change.
</ResponseField>

```json operation_created example theme={null}
{
  "event": "operation_created",
  "data": {
    "partnerId": "65f8e2f9c0a1d40012a3b4c5",
    "referenceId": "Ab3xY9mK",
    "id": "Ab3xY9mK",
    "userReferenceId": "user-8842",
    "amount": 150.5,
    "currency": "GTQ",
    "expiresInMinutes": 120,
    "status": "pending",
    "statusDetails": "The operation is pending because the sender has not yet started the process.",
    "waLink": "https://wa.me/502123456789?text=...#Ab3xY9mK",
    "landingLink": "https://landing.example.com/r/Ab3xY9mK",
    "requestData": {
      "userReferenceId": "user-8842",
      "name": { "firstName": "Maria", "lastName": "Lopez" },
      "address": { "country": "GT", "stateCode": "GT-01", "city": "Guatemala City", "line1": "12 Av. Reforma" },
      "dob": "1990-05-15",
      "payoutMethod": {
        "type": "bank_account",
        "bankAccount": {
          "country": "GT",
          "stateCode": "GT-01",
          "accountNumber": "0123456789012",
          "accountType": "checking",
          "bankCode": "BIND",
          "bankName": "BANCO INDUSTRIAL"
        }
      }
    },
    "createdAt": "2026-05-06T10:00:00.000Z",
    "updatedAt": "2026-05-06T10:00:00.000Z"
  },
  "timestamp": "2026-05-06T10:00:00.000Z"
}
```

## `operation_updated`

Emitted after a successful **`PATCH /v1/internal/remittances/status`** for one of your remittances. The `data` object has the same shape as `operation_created`; `status`, `statusDetails`, and `updatedAt` reflect the new state.

```json operation_updated example theme={null}
{
  "event": "operation_updated",
  "data": {
    "partnerId": "65f8e2f9c0a1d40012a3b4c5",
    "referenceId": "Ab3xY9mK",
    "id": "Ab3xY9mK",
    "userReferenceId": "user-8842",
    "amount": 150.5,
    "currency": "GTQ",
    "expiresInMinutes": 120,
    "status": "processing",
    "statusDetails": "Payer started the payment.",
    "waLink": "https://wa.me/502123456789?text=...#Ab3xY9mK",
    "landingLink": "https://landing.example.com/r/Ab3xY9mK",
    "requestData": { "...": "..." },
    "createdAt": "2026-05-06T10:00:00.000Z",
    "updatedAt": "2026-05-06T10:05:12.000Z"
  },
  "timestamp": "2026-05-06T10:05:12.000Z"
}
```

## `operation_error`

Emitted when the API throws while handling a request that targets one of your operations. The `data` object describes both the failing call and the error.

<ResponseField name="partnerId" type="string" required>
  Stable identifier of the partner that owns the operation.
</ResponseField>

<ResponseField name="path" type="string" required>
  HTTP path of the failing call (for example `/v1/request-money` or `/v1/internal/remittances/status`).
</ResponseField>

<ResponseField name="method" type="string" required>
  HTTP method of the failing call.
</ResponseField>

<ResponseField name="referenceId" type="string">
  Operation reference, when discoverable from the request body or URL.
</ResponseField>

<ResponseField name="errorCode" type="string" required>
  Stable identifier for the error class. Examples: `OFAC_HIT`, `BadRequestException`, `UnprocessableEntityException`, `ServiceUnavailableException`, `NotFoundException`, `Error`.
</ResponseField>

<ResponseField name="httpStatus" type="number" required>
  HTTP status code returned to the synchronous client. `500` for non-HTTP errors.
</ResponseField>

<ResponseField name="message" type="string" required>
  Human-readable description of the failure.
</ResponseField>

<ResponseField name="details" type="object | string">
  Structured error body when available — for example the OFAC `restrictionKey` or the validation report from the API. Absent for plain `Error` instances.
</ResponseField>

```json operation_error example (OFAC hit) theme={null}
{
  "event": "operation_error",
  "data": {
    "partnerId": "65f8e2f9c0a1d40012a3b4c5",
    "path": "/v1/request-money",
    "method": "POST",
    "referenceId": "Ab3xY9mK",
    "errorCode": "OFAC_HIT",
    "httpStatus": 403,
    "message": "Identity check rejected: global.watch.list.ofac",
    "details": { "restrictionKey": "global.watch.list.ofac" }
  },
  "timestamp": "2026-05-06T10:00:00.000Z"
}
```

```json operation_error example (validation) theme={null}
{
  "event": "operation_error",
  "data": {
    "partnerId": "65f8e2f9c0a1d40012a3b4c5",
    "path": "/v1/request-money",
    "method": "POST",
    "errorCode": "UnprocessableEntityException",
    "httpStatus": 422,
    "message": "Validation failed",
    "details": {
      "statusCode": 422,
      "message": ["referenceId must match /^[A-Za-z0-9]{8}$/"],
      "error": "Unprocessable Entity"
    }
  },
  "timestamp": "2026-05-06T10:00:00.000Z"
}
```

<Note>
  `operation_error` fires whenever a handled request raises an exception, including provider failures (for example a transient `503` from the identity validator). It is **independent** from the synchronous HTTP error your client receives — both happen.
</Note>

## Ordering and at-least-once delivery

Events are delivered **at least once**. Retries reuse the same `X-Webhook-Delivery-Id` so your handler can deduplicate. The dispatcher does not guarantee strict ordering across events; if you need a total order on a given operation, key off `data.referenceId` and break ties with `data.updatedAt` (or the envelope `timestamp` for `operation_error`).
