| 400 | Missing required field: <name> | Required field missing (paymentId, reason, requestedAmount.amount, requestedAmount.currency) | Fix the body, resubmit |
| 400 | Expected format: PAY_xxx, got "..." | paymentId Short ID could not be decoded | Fix paymentId format, resubmit |
| 400 | Invalid requestedAmount.amount format | Amount string could not be parsed | Use a display string like "10.50" (not minor units) |
| 400 | refundTicketMerchantExternalId must be at most 128 characters | Value exceeds 128 chars | Shorten to 128 chars or less |
| 401 | Unauthorized | Invalid API Key signature | Verify auth headers |
| 404 | Payment not found | Payment doesn’t exist or doesn’t belong to your store | Verify paymentId |
| 409 | Payment status is X, must be succeeded | Original payment is not in succeeded state | Do not retry — the payment never succeeded |
| 409 | Payment is not yet fully processed by PSP, please retry later | PSP confirmation hasn’t been written back yet | Retry after 5–30 seconds (transient) |
| 409 | Refund period expired (X days, max 14) | Past the 14-day window | Do not retry — refunds are no longer allowed |
| 409 | Requested amount must be greater than 0 | Amount is ≤ 0 | Fix the amount, resubmit |
| 409 | Requested amount exceeds payment amount | Amount > payment.amount | Fix the amount, resubmit |
| 409 | Currency mismatch: requested X, payment Y | Currency must match the original payment | Use the payment’s currency |
| 409 | A refund record already exists for this payment | A previous refund already succeeded against this payment | Do not retry — query existing refunds via GraphQL |
| 409 | A refund ticket already exists for this payment | Another in-flight ticket is blocking | Wait for the existing ticket to terminate, then resubmit if still needed |
| 500 | Internal server error | Unexpected server-side failure | Retry with exponential backoff (start 5s, max 3 attempts) |