# Payment Statuses

> Every MutoPay payment has one of ten statuses. This page lists them, explains when each fires, and shows the legal state transitions you should expect to see.

Source: https://mutopay.com/docs/payment-statuses/
Language: en

---

Every MutoPay payment has a `status` field that transitions through a small state machine. Webhooks fire on terminal states and on states that need merchant attention. This page documents every value and the transitions between them.

## Statuses

| Status | Terminal | Fires webhook | Meaning |
|---|---|---|---|
| `pending` | no | no | Payment created, customer has not started yet. |
| `awaiting_payment` | no | no | Customer picked a token, waiting for the transaction / manual transfer. |
| `confirming` | no | no | Transaction submitted, waiting for on-chain confirmation. |
| `processing` | no | no | Cross-chain swap/bridge order is executing. |
| `kyc_required` | no | ✅ `payment.kyc_required` | Swap provider asked the customer to complete KYC. Payment is paused, not failed. |
| `needs_manual_check` | no | ✅ `payment.needs_manual_check` | Unknown provider status. MutoPay support will investigate. |
| `completed` | ✅ | ✅ `payment.completed` | Funds settled in your wallet. |
| `failed` | ✅ | ✅ `payment.failed` | Payment failed. See `failure_reason`. |
| `expired` | ✅ | ✅ `payment.expired` | Customer did not complete before `expires_at`. |
| `underpaid` | ✅ | ✅ `payment.underpaid` | Customer sent less than the required amount. Every partial deposit (including subsequent top-ups) is recorded in `payer_deposits` on the payment object. |

## Transitions

```
pending ─▶ awaiting_payment ─▶ confirming ─▶ completed
                │                  │              │
                │                  ├─▶ processing ┤
                │                  │              │
                │                  │              ├─▶ failed
                │                  │              ├─▶ underpaid
                │                  │              └─▶ kyc_required ─▶ processing ─▶ completed
                │                  │                                              ╲─▶ failed
                │                  └─▶ needs_manual_check ─▶ (resolved off-band)
                └─▶ expired (anytime before terminal)
```

Key rules:

- A payment can only transition to `completed`, `failed`, `expired`, or `underpaid` **once**. Those are terminal.
- `kyc_required` and `needs_manual_check` are **recoverable**: they can transition back to `processing`, then continue to `completed` or `failed`.
- **Recovery transitions fire no webhook.** When `kyc_required` clears back to `processing`, your webhook endpoint will not hear about it. Your signal is the eventual `payment.completed` or `payment.failed`.

## Handling non-terminal alert statuses

For `kyc_required`:

- Inform the customer that the swap provider requested identity verification. You may want to keep the order "pending" in your own system rather than cancelling it. It can still complete.
- The customer's next action happens off-MutoPay (in the swap provider's flow).

For `needs_manual_check`:

- Support will contact you if action is needed. Don't auto-cancel the order: payment may still complete.
- Check `failure_reason` for a hint.

## Idempotency for webhook handlers

Since a payment can fire multiple webhooks across its lifetime (`kyc_required` → eventually `completed`, for example), your handler must be idempotent. The simplest pattern:

```
dedupe_key = payment_id + ":" + event
if already_processed(dedupe_key):
    return 200  // still ACK, so MutoPay stops retrying
record(dedupe_key)
handle(event)
```

## See also

- [Webhooks](/docs/webhooks/): event list and payload.
- [Create a Payment](/docs/create-payment/): where payments begin.
