Returns coordinate authorization, merchandise, and buyer value without treating a refund as proof that goods came back. A Return contains frozen order-line identity, policy evaluation, line-level quantities, handoff requirements, progress summaries, completion blockers, and links to its operational and financial effects.
Use the eligibility branch of POST /v1/return-previews to discover returnable fulfilled allocations, then create a Return with POST /v1/returns. Apply receipt, inspection, disposition, and resolution facts to that Return with the individual operation routes or POST /v1/returns/{return_id}/process.
New to this surface? The Returns guide explains the model and points at the right integration; Your first return walks one item from a paid order to a settled refund.
The four statuses#
A Return carries four status fields because the things they describe genuinely come apart. Read the one that answers your question rather than inferring from a neighbor.
| Field | Question it answers | Values |
|---|---|---|
decision_status | Did we say yes? | pending, approved, partially_approved, declined |
merchandise_status | Did the goods come back? | not_required, awaiting_handoff, in_transit, partially_received, received, inspection_required, partially_inspected, inspection_review_required, disposition_required, resolved, exception |
resolution_status | Did the buyer get their value? | not_selected, pending, partially_fulfilled, requires_action, fulfilled, failed |
status | Is the whole thing finished? | requested, open, declined, canceled, completed |
pending appears in three of these and fulfilled in two, so a bare status value is ambiguous without its field name. merchandise_status is not_required for a returnless refund, where the buyer keeps the goods, and exception when merchandise arrived that Flint cannot attribute to a line.
A Return moves requested to open on decision and open to completed when the last obligation clears. completed is not terminal: /reopen returns it to open so late compensating facts can be recorded.
Completion blockers#
completion_blockers is the authoritative list of what is still owed. Each entry names the Return line it belongs to and, where one exists, the receipt, inspection, disposition, or resolution holding it up. An empty array means nothing is outstanding.
| Code | Cleared by |
|---|---|
decision_pending | Deciding the line |
handoff_pending | The buyer handing merchandise to a carrier |
receipt_pending | The warehouse recording arrival |
inspection_pending | An inspection observation |
inspection_review_required | An acceptance decision on an inspection line |
disposition_required | A successful disposition |
resolution_not_selected | Choosing what the buyer gets |
resolution_pending | The resolution's effects settling |
resolution_requires_action | Whatever the resolution is waiting on, usually a buyer payment |
resolution_failed | Retrying or replacing the failed resolution |
merchandise_exception | Verifying or dispositioning merchandise that arrived unidentified or in excess |
Under completion_mode: "automatic" Flint completes the Return when the final blocker clears. Under manual you call /complete, and the call fails while any blocker remains.
Gate downstream work on return.completed or on an empty blocker list, never on a refund succeeding. A resolution can reach a terminal state while merchandise work is still open.
Line quantities#
A Return line carries twelve counters. Use the counters to track the return's quantities and progress.
| Question | Read |
|---|---|
| How much can I still approve? | requested_quantity minus approved_quantity, declined_quantity, and canceled_quantity |
| How much is still expected back? | return_required_quantity minus received_quantity |
| How much can I still put on a resolution? | available_resolution_quantity |
available_resolution_quantity is derived. Proposed, pending, action-required, failed, and fulfilled resolutions all reserve capacity. A failed resolution does not release its quantity until it is canceled or corrected, which is what stops a retry and a replacement from both paying out. Canceling an unpaid exchange closes its replacement Order and releases its return credit for a new resolution.
return_required_quantity is the returnless-refund control. Set it to 0 while approving quantity and the buyer keeps the merchandise; the Return still settles the money and still records why.
The remaining counters (handed_off_quantity, inspected_quantity, accepted_quantity, rejected_quantity, dispositioned_quantity, resolved_quantity, review_required_quantity) track progress through the physical steps. Disposition capacity is received_quantity when no inspection is required, and accepted_quantity plus rejected_quantity when one is.
Writes#
Return PATCH routes preserve omitted fields. Send null to clear a nullable scalar that the route exposes, including external_reference_id, buyer_note, and requested_resolution_type.
Child mutations on /line-items return the parent Return rather than the mutated line, because a line change can move the Return's aggregate statuses and blockers. GET on the same path returns the line itself.
Resolution line_items and replacement_line_items are owned arrays. Replace either array through PATCH /v1/return-resolutions/{return_resolution_id} with the resolution's expected_version. Retained members keep their stable child IDs; omitting an array leaves it unchanged.
Writes that change meaning take expected_version from the resource you last read. On a conflict, 409 RETURN_VERSION_CONFLICT carries the current version in its error detail, so you can re-read and retry rather than searching for what changed.
Idempotency-Key is required on POST /v1/returns/{return_id}/process and optional elsewhere. Replaying the same processing key returns the original result without repeating inventory or money effects.
Processing a Return requires all three scopes: commerce.returns.write, commerce.returns.operations.write, and commerce.returns.resolutions.write. Decisions, receipts, inspections, dispositions, and resolutions are recorded in one request. Refunds, payments, replacement orders, and inventory updates complete asynchronously. expected_version is optional on the process route and checked only when sent.
Link the buyer to a Return#
When you send your own Return email, link the buyer to the Return with POST /v1/returns/{return_id}/access-links. The url it returns opens the Return and its order in your Flint-hosted customer account without a sign-in, for 30 days or 10 opens. The url is a bearer credential: Flint returns it only in that response and in a retry with the same Idempotency-Key. The route needs commerce.returns.read, and refuses a merchant_hosted customer account and a Return whose order has no customer_id. See Link the buyer to their order.
curl -X POST https://api.withflintpay.com/v1/returns/ret_01J5Z8N3QK4W7Y2RB6TPVXHC9D/access-links \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: return-link-ret_01J5Z8N3QK4W7Y2RB6TPVXHC9D"
Buyer portal sessions#
A Return read with a portal session is narrower than the same Return read with a merchant key. supported_actions is trimmed to at most ["cancel"], and completion_blockers keeps the blockers a buyer can personally clear (handoff_pending, resolution_not_selected, resolution_requires_action, resolution_failed). A resolution_pending blocker is also included when the Return is in progress, the buyer owes a balance, and the named resolution can collect it in checkout. Other pending blockers and merchant-private fields are removed.
