Errors
A code to branch on, a message to act on, and a request id to quote.
Every error carries a code your program can branch on, a
message a person can act on, and a request_id to
quote if you need to ask us about it.
{
"error": {
"type": "invalid_request_error",
"code": "below_minimum_payment",
"message": "Order total 1.00 is below this account's minimum of 5.00.",
"param": "amount",
"request_id": "req_9f3aKd2mQx7b",
"doc_url": "https://payment.unifiwallet.com/docs/errors#below_minimum_payment"
}
}
Branch on code, never on message — messages are
written for humans and get clearer over time.
Status codes
| Status | Means |
400 | The request is wrong. Fix it; retrying will not help. |
401 | We could not authenticate you. Usually the signature. |
403 | We know who you are and this is not allowed. Scope, role or step-up. |
404 | No such thing, or it belongs to another account. We do not distinguish. |
409 | The thing exists but not in a state this works on. |
422 | Well-formed and refused on its content. |
429 | Too many. Read Retry-After. |
5xx | Ours. Retry with the same idempotency key. |
Authentication
| Code | What to do |
missing_signature | Send all three signing headers. |
timestamp_out_of_range | Your clock has drifted. Fix NTP. |
nonce_reused | Generate a fresh nonce per request. |
invalid_signature | Almost always the body: hash the exact bytes you send. |
insufficient_scope | The message names what is missing. Create a key that has it. |
signature_required | A bearer token was used where money moves. Sign the request. |
panel_only | Credentials and destinations need a person in the panel. |
Creating a payment
| Code | What to do |
no_settlement_destination | Register and verify a wallet first. |
settlement_destination_not_verified | Sign the verification message with that wallet. |
destination_in_cooling_period | Wait. The response says until when. |
below_minimum_payment | Under your floor. Network fees would cost more than the sale. |
domain_not_verified | Your return URL is on a domain you have not proved you own — or the DNS challenge does not match yet. |
live_not_enabled | Add your business details to take live payments. |
manual_capture_unavailable | Holds are not switched on for this deployment, or no escrow contract is deployed on your settlement chain. Use automatic. |
merchant_unavailable | Checkout: the merchant account is suspended. Nothing was charged. |
escrow_operation_in_flight | A capture or void is still confirming. Wait — do not retry, or the buyer could be charged twice. |
authorization_expired | The hold lapsed and the buyer may take the funds back. Ask them to pay again. |
capture_failed / void_failed | The transaction could not be submitted. Nothing moved. |
invalid_capture_amount | More than is still held. Check amount_capturable. |
deposit_address_unavailable | Checkout: no deposit address for a native coin. Pay from a wallet. |
Idempotency
| Code | What to do |
idempotency_key_required | Every POST needs one. |
request_in_progress | The first attempt is still running. Wait and retry the same key. |
idempotency_key_reuse | Same key, different body. Use a new key for a different request. |
Refunds and subscriptions
| Code | What to do |
payment_not_final | The payment is settled but not yet completed. Refund it once it is. |
refund_exceeds_settlement | More than arrived. The message says what remains. |
transfer_already_used | That transfer settled another refund. Send a separate one. |
allowance_below_cycle | The buyer's cap is under one cycle. Every renewal would decline. |
auto_charge_unavailable | No spend permission contract on that chain. Use a payment link. |
cycle_in_flight | Already being charged. Do not retry, or the buyer pays twice. A cycle a crash left behind is recovered within the hour. |
Compliance
These are refusals, not holds. Nothing has moved and nothing is stranded — the
checks run before the buyer signs, because non-custodially that is the only
moment we have.
| Code | Means |
payer_screening_failed | The paying address did not pass. The buyer can pay from another. |
destination_flagged | The settlement address cannot be used. |
screening_unavailable | We could not check. A temporary block, not a decision about you. |
When to retry
- Retry
5xx, 429 and network timeouts — same idempotency key, backing off.
- Do not retry
4xx. Nothing about sending it again changes the answer.
- Wait, then retry
request_in_progress with the same key.