# Webhook events

> Handle all 18 Paymos webhook event types with explicit resource state and terminal-state semantics.

Paymos publishes 18 event types: 9 for invoices, 6 for withdrawals and 3 for payment channels. Subscribe by category at the endpoint level; event names inside a category are not individually selectable.

## Invoice events

| Event | Meaning | Terminal |
|-------|---------|----------|
| `invoice.awaiting_payment` | A deep reorg removed every counted transfer and the invoice regressed to awaiting payment. The initial transition into awaiting payment does not emit this event | No |
| `invoice.confirming` | A payment was detected and is waiting for the required blockchain finality | No |
| `invoice.underpaid_waiting` | The received amount is below the expected amount and the invoice can still accept payment | No |
| `invoice.paid` | The expected amount was paid | Yes |
| `invoice.paid_over` | More than the expected amount was paid; the full received amount is credited | Yes |
| `invoice.underpaid` | The invoice reached a final underpaid state | Yes |
| `invoice.expired` | The invoice expired without a qualifying payment | Yes |
| `invoice.cancelled` | The merchant cancelled the invoice | Yes |
| `invoice.paid_late` | Money crossed the invoice's total after it had already closed as expired or underpaid. `data.status` keeps its terminal value, `data.paid_late_at` carries the mark time (Unix seconds). Treat it as a note, not a transition | No |

## Withdrawal events

| Event | Meaning | Terminal |
|-------|---------|----------|
| `withdrawal.created` | The withdrawal was accepted and created | No |
| `withdrawal.processing` | The outgoing transaction was observed on-chain and its transaction hash is available | No |
| `withdrawal.completed` | The outgoing transaction completed on-chain | Yes |
| `withdrawal.failed` | The withdrawal failed and reached a final state | Yes |
| `withdrawal.cancelled` | The withdrawal was cancelled before completion | Yes |
| `withdrawal.completion_rewound` | A chain reorg rewound the completed withdrawal and it is in flight again with status `signed`; a later event will complete or fail it | No |

## Payment channel events

| Event | Meaning | Terminal |
|-------|---------|----------|
| `payment_channel.deposit.confirming` | A transfer arrived at a channel address and is waiting for the required blockchain finality | No |
| `payment_channel.deposit.reorged` | A chain reorganization dropped the transaction. The same deposit can return to confirming if it is re-included | No |
| `payment_channel.deposit.confirmed` | The deposit settled and the net amount is credited to your balance | Yes |

There is no failed event: a deposit that reorgs and never returns simply stays `reorged`.

## Processing rules

- Route by the exact `event_type`; ignore unknown future types safely and alert for review.
- Read `data.status` and `data.is_final` from the payload instead of reconstructing state from the event name.
- Do not assume delivery order. Parallel delivery and retries can make a newer resource snapshot arrive first.
- A terminal event is final for that resource state machine. Repeated delivery of the same `event_id` is still a duplicate, not another transition.
- `invoice.paid_late` is a fact beside a terminal decision, never a transition: never mark an order paid, failed or cancelled on it. Add an order note (idempotent per invoice) and leave the decision as it was.
- Deduplicate business state on the resource id inside `data` (`inv_…`, `wdr_…`, `pcd_…`), which is stable across every transition and every endpoint. `event_id` identifies one delivery, not one payment.

See [Payload contract](/docs/webhooks/payloads.md) for the common envelope and resource fields.