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.

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).

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

HTTP status codes

StatusBody errorMeaning & fix
400invalid_requestValidation error. Check message — a field is missing, malformed, or out of range.
401unauthorizedMissing 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.
403merchant suspendedYour account is suspended. Contact support.
403forbiddenYou 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).
404not_foundThe payment/channel/resource doesn’t exist, or has been deleted.
409conflictState conflict — e.g. trying to create an admin when one already exists, or submitting an order for a payment that’s already completed.
422unprocessableBusiness-logic rejection — e.g. the selected token/chain combination is unsupported, or the amount is below the minimum for the chosen route.
429rate_limitedToo many requests. Back off and retry with exponential delay.
500internal_errorOur fault. Retry idempotent reads; for writes, check whether the operation succeeded before retrying.
502/503/504Transient 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.

”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 → 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 (if active).

See also