# Errors & Troubleshooting

> MutoPay uses standard HTTP status codes with structured JSON error bodies. This page lists every error, what it means, and how to fix it.

Source: https://mutopay.com/docs/errors/
Language: en

---

MutoPay returns standard HTTP status codes. Every error body is JSON with at least an `error` field (machine-readable) and usually a `message` field (human-readable).

```json
{
  "error": "invalid_request",
  "message": "amount_usd must be greater than 0"
}
```

## HTTP status codes

| Status | Body `error` | Meaning & fix |
|---|---|---|
| 400 | `invalid_request` | Validation error. Check `message`: a field is missing, malformed, or out of range. |
| 401 | `unauthorized` | Missing or invalid API key. Check the header name (`X-API-Key` for channel, `Authorization: Bearer` for master) and that the key hasn't been rotated. |
| 403 | `merchant suspended` | Your account is suspended. Contact support. |
| 403 | `forbidden` | You are trying to access a resource that doesn't belong to you (e.g. `GET /api/merchant/payments/pay_abc` where `pay_abc` belongs to another merchant). |
| 404 | `not_found` | The payment/channel/resource doesn't exist, or has been deleted. |
| 409 | `conflict` | State conflict (e.g. trying to create an admin when one already exists, or submitting an order for a payment that's already completed). |
| 422 | `unprocessable` | Business-logic rejection (e.g. the selected token/chain combination is unsupported, or the amount is below the minimum for the chosen route). |
| 429 | `rate_limited` | Too many requests. Back off and retry with exponential delay. |
| 500 | `internal_error` | Our fault. Retry idempotent reads; for writes, check whether the operation succeeded before retrying. |
| 502/503/504 | - | Transient infrastructure error. Retry with backoff. |

## Common gotchas

### "invalid signature" on webhook verification

- Are you using the **raw** request body bytes (not a re-serialized JSON)? Re-serializing changes whitespace and key order.
- Are you using **the webhook secret for that channel** (not the merchant's master key or a different channel's secret)?
- Is your comparison **constant-time**? See [Verify a webhook signature](/docs/verify-webhook-signature/).

### "Minimum invoice on this channel is $N.NN" (400)

Your `amount_usd` is below the channel's minimum. The exact figure is in the response `message`. A few notes:

- The minimum is **per channel** and recomputes hourly from current network conditions. Don't hardcode a fixed value on your side, read the threshold out of `message` and surface it to the customer.
- It applies even when you submit `amount_original` + `currency`. The server converts to USD before checking.
- A safe pattern: catch the 400, parse the dollar figure from `message`, show "Minimum order: $N" on your checkout. Update on every reject so you keep up with rate changes.

### "payment expired" with a fresh payment

- `expires_at` defaults to 30 minutes from creation. Long-running checkout flows should pass an explicit `expires_at` when creating the payment.
- Set it in your call: `"expires_at": "2026-04-21T16:00:00Z"`. Max is 7 days.

### "unauthorized" but the key is correct

- Channel keys go in `X-API-Key`. Master keys go in `Authorization: Bearer`. Swapping these returns 401.
- Keys are shown **once** at creation. If you lost it, rotate and update your integration.

### "merchant suspended" (403)

- Someone at MutoPay has paused your account. Email support with your merchant ID (the `id` you see in the dashboard URL, or returned on any `/api/merchant/*` response).

### Webhook never arrives

1. Is the webhook URL configured on the channel that created the payment? Each channel has its own URL.
2. Is your endpoint **reachable from the public internet** and served over HTTPS?
3. Check [Settings → Channels](https://mutopay.com/dashboard/settings) → test webhook. If the test fails, the issue is on your side.
4. Returning a non-2xx? MutoPay retries 5 times over ~15 hours, then gives up. Check your server logs.

## Getting help

- For integration issues: hello@mutopay.com with your merchant ID, the payment ID, and the full request/response including headers (redact keys).
- For account suspension or billing: reply to the email we sent you, or same address.
- Status page: [status.mutopay.com](https://status.mutopay.com) (if active).

## See also

- [Authentication](/docs/authentication/): which key to use where.
- [Verify a webhook signature](/docs/verify-webhook-signature/): fixes most webhook auth errors.
