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

# Events

> Webhook event types and the transaction lifecycle

## Event types

You choose which events to subscribe to per endpoint, so you can listen only for the ones you care about (most integrations just need `transaction.completed` and `transaction.failed`).

### Transaction events

Each event maps 1:1 to a transaction status.

| Event                    | Status       | When it's sent                                                                                                                                        |
| ------------------------ | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `transaction.pending`    | `pending`    | The transaction has been created and is awaiting processing. For a payout, funds have been debited; for a deposit, incoming funds have been detected. |
| `transaction.processing` | `processing` | The transaction is actively being processed (submitted to the bank/network).                                                                          |
| `transaction.sent`       | `sent`       | Funds have been dispatched to the provider/network and are in flight (not yet confirmed final).                                                       |
| `transaction.completed`  | `completed`  | The transaction finalized successfully. For a deposit, funds are available in your wallet; for a payout, the beneficiary has been paid.               |
| `transaction.failed`     | `failed`     | The transaction could not be completed. Funds for a failed payout are returned to your wallet.                                                        |
| `transaction.rejected`   | `rejected`   | The transaction was rejected (e.g. declined during approval or compliance review).                                                                    |
| `transaction.refunded`   | `refunded`   | A previously completed transaction was reversed/refunded.                                                                                             |

### Fee events

A fee your account bears for an account you manage moves through three stages, and each is announced separately. Subscribe to all three, or only to the one you act on.

| Event          | `effect`   | When it's sent                                                                                                                                        |
| -------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fee.pending`  | `held`     | The fee has been placed as a hold. **Your available balance already reflects it** — this is when the money becomes unavailable, not when it is taken. |
| `fee.charged`  | `charged`  | The fee posted. The money has left.                                                                                                                   |
| `fee.released` | `released` | The transaction that incurred the fee was unwound, so the hold is released and nothing was charged. Reverse any accrual you made on `fee.pending`.    |

<Warning>
  If you subscribe to `fee.charged` alone, your balance will move without an event to explain it — sometimes for a long time, since a fee is held for as long as the transaction that incurred it stays unsettled. Take `fee.pending` and `fee.released` too if you reconcile on available balance.
</Warning>

```json theme={null}
{
  "event": "fee.charged",
  "event_id": "5f2c1a9e-7b3d-4a8e-9c1f-2d3e4b5a6c7d",
  "created_at": "2026-08-19T10:00:01.000Z",
  "data": {
    "transaction_id": "c1b2a3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "reference": "FEE-8KD02M1X",
    "status": "completed",
    "effect": "charged",
    "amount": 25000,
    "currency": "NGN",
    "fee_type": "withdrawal",
    "description": "Fee for sub-account withdrawal",
    "related_transaction_id": "b0a1c2d3-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "related_reference": "NG-J46RHHKV",
    "incurred_by_account": "eec3cbed-79d8-4370-87a0-b6be9e287337",
    "created_at": "2026-08-19T10:00:01.000Z"
  }
}
```

All three carry the same shape; `event` and `effect` say which stage it is. `amount` is in **minor units** — `25000` means 250.00. Note the transaction listing endpoints report amounts in major units instead, so convert before comparing the two. `fee_type` is what the fee was charged for — `withdrawal`, `payin` or `card_funding`. `related_transaction_id` is the movement that incurred it and `incurred_by_account` the account that made it, so the charge can be attributed without matching on amount or timing.

Each stage carries its own `event_id`, so a redelivery of the same stage is recognisable while the three stages never suppress one another.

<Note>
  A fee borne by the account that incurred it does not produce these events — there is no separate charge to announce, and the fee is reported as `fee_amount` on that account's own transaction. To list fees rather than be pushed them, filter [List Transactions Across Accounts](/api-reference/endpoint/accounts/list-transactions) on `transactionType=fee`.
</Note>

### Account events

These cover the onboarding lifecycle of the accounts you manage via the API (the additional business/individual accounts you create under your profile), plus the deposit accounts provisioned for them.

| Event                                       | When it's sent                                                                                                                                                                                       |
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `account.onboarded`                         | An account was created (onboarding started).                                                                                                                                                         |
| `account.submitted`                         | The account's KYB/KYC application was submitted for review.                                                                                                                                          |
| `account.approved`                          | The account's application was approved and it's ready to transact.                                                                                                                                   |
| `account.rejected`                          | The account's application was rejected during review.                                                                                                                                                |
| `account.changes_requested`                 | The application was reviewed and changes were requested — the account must update its details and resubmit.                                                                                          |
| `account.virtual_account.created`           | A deposit (virtual) account — bank transfer or crypto — was provisioned for the account.                                                                                                             |
| `account.virtual_account.changes_requested` | A deposit account request needs corrected details before it can proceed. Re-issue with the corrections to reopen it.                                                                                 |
| `account.virtual_account.declined`          | A deposit account request was declined. `data.bank_account_request.reason` carries why.                                                                                                              |
| `account.verification.completed`            | An identity check finished. Sent for the account holder on an individual account, and once per beneficial owner on a business. `data.ready_to_submit` says whether the account can now be submitted. |

## Account lifecycle

An account onboarded through the API typically moves through:

<Steps>
  <Step title="account.onboarded">
    The account has been created and onboarding has begun. Complete its application data and upload the required documents.
  </Step>

  <Step title="account.submitted">
    The application has been submitted for review.
  </Step>

  <Step title="account.approved / account.rejected / account.changes_requested (review outcome)">
    Review resolves one of three ways: **approved** (the account can now transact, and deposit accounts can be issued for it), **rejected** (the application was declined), or **changes\_requested** (the account must update its details and resubmit — after which it emits `account.submitted` again).
  </Step>

  <Step title="account.virtual_account.created">
    A deposit account (NGN/USD bank transfer details, or a crypto address) is now available for funding. The `data.virtual_account` object carries the details — the same shape you'd get from the [funding instructions](/api-reference/endpoint/accounts/funding-instructions) endpoint.
  </Step>
</Steps>

## Deposit account requests

A USD deposit account request is reviewed before the account is provisioned, so it resolves one of three ways:

<Steps>
  <Step title="account.virtual_account.created">
    The request was approved and the deposit account exists.
  </Step>

  <Step title="account.virtual_account.changes_requested">
    Something on the request needs correcting. `data.bank_account_request.reason` explains what. Re-issue the account with the corrections and the same request reopens — no duplicate is created.
  </Step>

  <Step title="account.virtual_account.declined">
    The request was declined, with the reason in `data.bank_account_request.reason`. Further requests for the same currency are blocked until the decision is lifted.
  </Step>
</Steps>

<Info>
  These three events are scoped to the **deposit account request**, not the account's KYB. `account.rejected` and `account.changes_requested` refer to the account's application and are unrelated.
</Info>

## Transaction lifecycle

A typical transaction emits `transaction.pending` and then one or more later events as it progresses to a terminal state:

<Steps>
  <Step title="transaction.pending">
    The transaction has been created and is awaiting processing.
  </Step>

  <Step title="transaction.processing / transaction.sent (in flight)">
    The transaction is being processed and dispatched to the bank or network.
  </Step>

  <Step title="transaction.completed (success path)">
    The transaction finalized. For a deposit, funds are now available in your wallet; for a payout, the beneficiary has been paid.
  </Step>

  <Step title="transaction.failed / rejected (failure paths)">
    The transaction did not complete. Funds for a failed/rejected payout are returned to your wallet. A `transaction.refunded` event may follow a completed transaction that is later reversed.
  </Step>
</Steps>

<Info>
  Not every transaction emits every event, and intermediate events (`processing`, `sent`) may be skipped depending on the rail and how fast the transaction settles. A deposit that is auto-confirmed may jump from `pending` to `completed`; a payout that fails will emit `transaction.pending` then `transaction.failed`. Always treat the event's status as the source of truth and don't assume ordering.
</Info>

## The `event_id` field

Each logical event has a stable `event_id`. The **same** logical event (for example, "transaction X completed") always carries the **same** `event_id`, even if it is delivered more than once. Use it to make your handler [idempotent](/api-reference/webhooks/delivery#idempotency).

## Distinguishing deposits from payouts

The payload's `data.type` field is either `deposit` or `payout`. The `data.rail` field tells you whether it's `fiat` or `stablecoin`. See [Payloads](/api-reference/webhooks/payloads) for the full schema and samples.
