Errors
Handle every error the API can return. Each one has a stable code to branch on. The table below says what to do about each code and whether a retry is safe.
Available at launch
Error format#
Errors use RFC 9457 problem details, with Content-Type: application/problem+json.
{
"type": "https://docs.unplugdining.com/reference/errors#insufficient_points",
"title": "Not enough points",
"status": 422,
"code": "insufficient_points",
"detail": "This reward costs 500 Beans. The member has 420.",
"param": "reward_id",
"request_id": "req_7Hq2kD9xLm"
}| Field | Description |
|---|---|
typestring | A link to this code's row in the table below. |
titlestring | Short summary of the code. The same for every error with this code. |
statusinteger | The HTTP status. |
codestring | Branch on this. It never changes for a given error. The list may grow. |
detailstring | What went wrong in this request, for your logs. Written for developers, not guests. |
paramstring or null | The field or header that caused it, when there is one. |
request_idstring | Same as the Request-Id header. Quote it to support. |
Some errors add a field. order_already_exists adds order_id, the order that was already credited. Ignore fields you don't know.
Don't show detail to guests. For a reward or code that doesn't apply, show the message from the reason codes in a checkout preview instead.
HTTP status codes#
| Status | Meaning |
|---|---|
200 | OK. |
201 | Created. |
202 | Accepted. The work finishes later, for example a member deletion. |
204 | OK, with no body. |
400 | The request is malformed: a missing or invalid field or header. Fix it before you send it again. |
401 | The key or member token is missing, wrong, revoked or expired. |
403 | The credential is valid but not allowed to do this. |
404 | The thing you asked for doesn't exist in your brand. |
409 | Conflicts with the current state: a duplicate, a request still running, or an action the object's status doesn't allow. |
410 | The member was deleted. |
422 | The request is well formed but breaks a business rule, such as not enough points. |
429 | Too many requests. Wait for Retry-After seconds. See Rate limits. |
500 | Something failed on our side. |
503 | The API is briefly unavailable. |
Retrying#
Retry 429 and every 5xx with the same Idempotency-Key: the request either didn't run or will be replayed, so nothing happens twice. Don't retry other 4xx errors unchanged; they fail the same way until you fix the request. 409 request_in_progress is the one exception: retry it after a second. See the retry recipe.
Error codes#
| Code | Status | What it means | What to do | Retry safe |
|---|---|---|---|---|
invalid_request | 400 | The body isn't valid JSON, or fields that go together are wrong, for example two of reward_id, wallet_item_id and code. | Fix the request. | No |
invalid_param | 400 | A field has the wrong type or format. param names it. | Fix the field. | No |
missing_param | 400 | A required field is missing. param names it. | Add the field. | No |
idempotency_key_required | 400 | This endpoint needs an Idempotency-Key header. | Add one. See Idempotency. | No |
invalid_api_key | 401 | The key is missing, wrong or revoked, or a test key was sent to production (or a live key to the sandbox). | Check the key and host. | No |
token_expired | 401 | The member token is past its 15 minutes or was revoked. | Ask your server for a new token, then retry. | Yes, with a new token |
missing_scope | 403 | The key lacks the scope this endpoint needs, or a member token called an endpoint it can't use. | Use a key with the scope, or call from your server. | No |
not_found | 404 | No such endpoint, or no object with this id in your brand. | Check the path and id. | No |
member_not_found | 404 | No member with this id or external_id. | Create the member, or look them up by external_id. | No |
reward_not_found | 404 | No reward or wallet item with this id. | Refresh the catalog or wallet. | No |
location_not_found | 404 | No location with this id in your brand. | Refresh locations. | No |
member_deleted | 410 | The member was deleted. | Unlink them from your user record. Create a new member if they sign up again. | No |
phone_in_use | 409 | Another member with an external_id has this phone. | Ask the guest to sign in to their existing account, or save the member without the phone. | No |
request_in_progress | 409 | A request with this Idempotency-Key is still running. | Wait a second and retry with the same key. | Yes, after 1 second |
order_already_exists | 409 | An order with this external_order_id was already credited. order_id is in the body. | Treat it as success and use order_id. | No |
invalid_state | 409 | The object's status doesn't allow this action, for example publishing an offer that isn't a draft, or redelivering to a disabled webhook endpoint. detail says which status it's in. | Read the object, then act on its current status. | No |
redemption_not_reserved | 409 | The redemption is already captured, canceled or expired. | Read it with GET /v1/redemptions/{id}. Refund the order to undo a captured one. | No |
external_id_taken | 422 | Another member already has this external_id. | Look up the existing member with ?external_id=. | No |
idempotency_key_reused | 422 | This key was already used with a different body. | Use a new key for a new request. If this is a retry, send the original body. | No |
insufficient_points | 422 | The member can't afford the reward, or a negative adjustment is larger than the balance. | Show the balance. Preview the cart to list what they can afford. | No |
reward_not_applicable | 422 | The reward or wallet item doesn't apply to this cart. | Preview the cart and show the reason's message. | No |
reward_expired | 422 | The reward or wallet item has expired. | Refresh the wallet. | No |
reward_already_used | 422 | The wallet item was already used. | Refresh the wallet. | No |
code_invalid | 422 | No promo code with this value. | Ask the guest to check the code. | No |
code_expired | 422 | The code has ended. | Tell the guest the code has ended. | No |
code_limit_reached | 422 | The code has been used as many times as it allows, in total or by this member. | Tell the guest the code can't be used again. | No |
redemption_expired | 422 | An order referenced a redemption that is unknown, canceled or past its 15-minute hold. Nothing was written. | Send the order again without that id, or create a new redemption first. | No |
one_redemption_per_order | 422 | More than one redemption on one order. Stacking is coming soon. | Send one id in redemption_ids. | No |
refund_exceeds_order | 422 | Refunds would add up to more than the order. | Check earlier refunds with GET /v1/orders/{id}. | No |
challenge_not_joinable Coming soon | 422 | The challenge isn't opt-in, isn't live, or the member isn't eligible. | Refresh the member's challenges. | No |
rate_limited | 429 | Too many requests for this key or token. | Wait Retry-After seconds, then retry. | Yes |
internal_error | 500 | Something failed on our side. | Retry with backoff and the same key. If it keeps failing, email support with the request_id. | Yes |
service_unavailable | 503 | The API is briefly unavailable. | Retry with backoff and the same key. | Yes |