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

# Webhook events

> Every signed lifecycle event and its payload shape, as verified and typed by pulse.webhooks.verify / verifyOnce.

Every delivery is signed (`Pulse-Signature`, HMAC-SHA256 over the raw body),
at-least-once, and carries an `evt_*` id for de-duplication.
`pulse.webhooks.verify` / `verifyOnce` return the typed `WebhookEvent`. See
[Webhooks](/backend/webhooks) for the handler pattern and the
[node reference](/reference/node#webhooks) for the method signatures.

<Info>
  This surface reflects the `@pulsebyshiga/node` types — the working view of the API
  until the canonical Pulse OpenAPI contract is published. Wire shapes
  (snake\_case JSON, decimal-string amounts) are stable; a few fields are still
  being reconciled and are flagged where they appear.
</Info>

## Envelope

Every event shares this envelope; only `type` and the `data` shape differ.

<ResponseField name="id" type="string">`evt_*` — the idempotency key for consumers.</ResponseField>
<ResponseField name="type" type="WebhookEventType">The event name (the `##` headings below).</ResponseField>

<ResponseField name="created_at" type="string" />

<ResponseField name="data" type="object">Event-specific payload, documented per event below.</ResponseField>

<Warning>
  Credit a customer **only** after a verified `disbursement.completed` event —
  never off a client callback. See [Webhooks](/backend/webhooks).
</Warning>

## collection.order.created

<ResponseField name="data.order" type="CollectionOrder">
  The full order (see [CollectionOrder](/reference/node#response-collectionorder)).
</ResponseField>

## collection.deposit.detected

<ResponseField name="data.order_id" type="string" />

<ResponseField name="data.deposit" type="Deposit">
  <Expandable title="Deposit">
    <ResponseField name="tx_hash" type="string" />

    <ResponseField name="amount" type="string">On-chain detected amount — the credited figure, never the quoted one.</ResponseField>

    <ResponseField name="asset" type="&#x22;USDC&#x22; | &#x22;USDT&#x22;" />

    <ResponseField name="network" type="Network" />

    <ResponseField name="detected_at" type="string" />

    <ResponseField name="confirmed_at" type="string | null" />
  </Expandable>
</ResponseField>

## collection.deposit.confirmed

<ResponseField name="data.order_id" type="string" />

<ResponseField name="data.deposit" type="Deposit">
  Same `Deposit` shape as `collection.deposit.detected`; `confirmed_at` is now
  set.
</ResponseField>

## collection.deposit.wrong\_chain

<Info>
  Provisional contract shape (PRD wrong-chain row) — the real detection signal
  comes from Pulse and the recovery policy is still being decided. Treat as
  subject to change until the canonical contract publishes.
</Info>

<ResponseField name="data.order_id" type="string" />

<ResponseField name="data.deposit" type="WrongChainDeposit">
  <Expandable title="WrongChainDeposit">
    <ResponseField name="tx_hash" type="string" />

    <ResponseField name="amount" type="string" />

    <ResponseField name="asset" type="&#x22;USDC&#x22; | &#x22;USDT&#x22;" />

    <ResponseField name="network" type="string">Network the funds actually arrived on (may be outside the supported set).</ResponseField>

    <ResponseField name="detected_at" type="string" />
  </Expandable>
</ResponseField>

<ResponseField name="data.expected_network" type="Network">
  The network the order expected payment on.
</ResponseField>

## collection.amount.underpaid

<ResponseField name="data.order_id" type="string" />

<ResponseField name="data.expected" type="string">Expected amount, decimal string.</ResponseField>
<ResponseField name="data.received" type="string">Received amount, decimal string.</ResponseField>

<ResponseField name="data.asset" type="&#x22;USDC&#x22; | &#x22;USDT&#x22;" />

<ResponseField name="data.network" type="Network" />

<ResponseField name="data.tx_hash" type="string" />

## collection.amount.overpaid

<ResponseField name="data" type="AmountMismatchData">
  Same shape as `collection.amount.underpaid`: `order_id`, `expected`,
  `received`, `asset`, `network`, `tx_hash`.
</ResponseField>

## collection.order.expired

<ResponseField name="data.order_id" type="string" />

## collection.order.failed

<ResponseField name="data.order_id" type="string" />

<ResponseField name="data.reason" type="string" />

## disbursement.completed

The only event you should credit a customer from.

<ResponseField name="data.order_id" type="string" />

<ResponseField name="data.disbursement" type="Disbursement">
  <Expandable title="Disbursement">
    <ResponseField name="settlement_ref" type="string" />

    <ResponseField name="currency" type="&#x22;NGN&#x22; | &#x22;USD&#x22;" />

    <ResponseField name="amount" type="string" />

    <ResponseField name="virtual_account_ref" type="string | null">Naira gate only; null for USD (partner omnibus).</ResponseField>

    <ResponseField name="completed_at" type="string" />
  </Expandable>
</ResponseField>
