Approvals
Some outlets ask a manager to approve a return, a refund or an exchange. The manager proves it with a PIN or a one-time code. You then send the approval_token you got with the return, refund or exchange. See Returns, Refunds and Exchanges.
All approval calls need X-Outlet-ID and X-User-ID, and the acting user's role must allow verifying approvals (the same permission the cashier app checks). The PIN and one-time code calls also need the user to have an open shift at the outlet, otherwise they answer 409 SHIFT_NOT_OPEN. GET /approvals/eligible-approvers is a read and needs no open shift. Approval calls take no Idempotency-Key.
When Approval Is Needed
Each outlet decides, for each type (refund, return, exchange, order_cancel), whether an approval is needed and how it is given. A return, refund or exchange sent without a token answers 403 APPROVAL_REQUIRED when approval is needed. When it is not needed, the token is not required.
Check first with GET /approvals/eligible-approvers?types=refund (a comma list, for example types=refund,return):
is_enabledisfalse: no approval is needed. Skip the rest.policyis how approval is given:pin,otp_preferredorotp_only. With several types it is the strictest one.pin: a manager PIN.otp_preferred: a one-time code is preferred, a PIN is also accepted.otp_only: a one-time code is needed. A PIN is accepted only after the code could not be sent (see below).
cancellation_typeslists the asked types that need an approval.approverslists who can approve:approver_id,name,role_captionandcan_receive_otp. Whencan_receive_otpisfalse,otp_unavailable_reasonisno_phoneorphone_unverified.
The approver's phone number is never returned by any approval call.
PIN Flow
- Send
POST /approvals/pinwith the manager'spin(4 to 8 digits), theorder_idof the paid order, thecancellation_types(at least one) and agate_reason. Sendapprover_idto check one approver; leave it out to accept any approver of the outlet whose PIN matches. - The answer has an
approval_token,approver_id,approver_name, theorder_id,expires_at,methodand thecancellation_typesit covers. - Send the token as
approval_tokenon the return, refund or exchange.
method in the answer is pin, otp or pin_fallback.
One-Time Code Flow
- Send
POST /approvals/otpwithapprover_id(one withcan_receive_otp),order_id,cancellation_typesandgate_reason. The answer has arequest_id,expires_at,resend_after,sends_usedandsends_max. - The approver reads the 6 digit code. It works for 5 minutes.
- Send
POST /approvals/otp/verifywith therequest_idand thecode. The answer is the same as the PIN flow, withmethodotp.
Resend. Send the same body to POST /approvals/otp again to send a new code. At most 3 codes can be sent for the same order, user and types. Wait until resend_after: before that the call answers 409 OTP_RESEND_COOLDOWN and data.resend_after tells the time. This API never answers 429.
When no code can be sent. 409 OTP_UNAVAILABLE means no code can be sent now. data.reason tells why (for example no_reachable_approver, quota_exhausted, provider_error, cashier_cap_reached or resend_exhausted). The answer also has data.fallback_authorized (true) and data.fallback_expires_at: until that time you may use the PIN flow, and the token has method pin_fallback.
Token Rules
- It is valid for 5 minutes (
expires_at). - It belongs to one order (
order_id) and one user (theX-User-IDthat asked for it). - It covers only the
cancellation_typeslisted in the answer. - It works once. Ask for a new one for the next operation.
Lockout
After 3 wrong PINs or codes for the same order and gate_reason at the outlet (counted for all users together), the check is locked for 15 minutes. The answer is 409 PIN_LOCKED with data.unlock_at. A wrong PIN answers 403 PIN_INVALID and a wrong code 403 OTP_INVALID; both have data.attempts_remaining.
Never store PINs. Do not keep a manager's PIN in your system, your logs or your database. Ask for it, send it once, and drop it. Keep only the approval_token, and only until it is used.
Error Codes
| HTTP | Code | When |
|---|---|---|
400 | VALIDATION_FAILED | A field is missing or wrong, or types is missing or has an unknown value |
403 | PIN_INVALID | The PIN is wrong. data.attempts_remaining tells how many tries are left |
403 | OTP_REQUIRED | The outlet needs a one-time code and no PIN fallback is open. Request a code first |
403 | OTP_INVALID | The code is wrong. data.attempts_remaining |
403 | OTP_EXPIRED | The code expired, or the request_id is unknown or belongs to another user |
403 | OTP_NOT_ENABLED | The outlet does not use approval by one-time code |
403 | PLAN_FEATURE_REQUIRED | The merchant's plan has no approval by one-time code |
403 | APPROVER_NOT_ELIGIBLE | The approver is not on the outlet's approval line, or cannot receive a code while another can |
404 | NOT_FOUND | The order_id is unknown or is an order of another outlet |
409 | SHIFT_NOT_OPEN | The acting user has no open shift at the outlet (PIN and one-time code calls) |
409 | PIN_LOCKED | Too many wrong tries. data.unlock_at tells when to try again |
409 | OTP_RESEND_COOLDOWN | A code was sent a moment ago. data.resend_after |
409 | OTP_UNAVAILABLE | No code can be sent now. A PIN fallback is open (see above) |
These errors from the return, refund and exchange calls are about the token: 403 APPROVAL_REQUIRED (no token), 403 APPROVER_NOT_ELIGIBLE (the approver is not allowed) and 403 APPROVAL_TOKEN_INVALID (unknown, used, expired, or issued for another order, user or type; data.reason tells which). See Errors.