Your first return

Three calls take a delivered item back and put the money on its way: find out what can come back, take it back, then watch it settle. Everything else on this surface is a variation on those three.

You need a sandbox-bound test key and an Order that has been paid and fulfilled. If you do not have one, accept a first payment and mark it fulfilled, then come back.

1

See what can come back#

Ask Flint what is still returnable on the order. This creates nothing and reserves nothing.

Bash
curl -X POST https://api.withflintpay.com/v1/return-eligibility-checks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "ord_1kmn0aExample",
    "selection": { "selection_type": "all_remaining_fulfilled" }
  }'
JSON
{
  "data": {
    "order_id": "ord_1kmn0aExample",
    "status": "eligible",
    "evaluated_at": "2026-07-29T14:20:00Z",
    "line_items": [
      {
        "order_line_item_id": "li_1kmn0aExample",
        "fulfillment_id": "ful_1kmn0aExample",
        "name": "Blue hoodie",
        "sku": "HOODIE-BLUE-M",
        "is_self_service_enabled": true,
        "eligibility": {
          "status": "eligible",
          "eligible_quantity": 1,
          "expires_at": "2026-08-28T14:20:00Z",
          "allowed_resolution_types": ["refund", "exchange"],
          "reason": "eligible_under_policy",
          "reason_message": "",
          "policy_adjustment_proposals": [],
          "evaluated_at": "2026-07-29T14:20:00Z"
        },
        "suggested_return_reasons": [
          {
            "return_reason_id": "rrsn_1kmn0aExample",
            "handle": "changed_mind",
            "name": "Changed mind",
            "is_note_required": false
          }
        ]
      }
    ]
  },
  "request_id": "req_1kmn0aExample"
}

You now have the three things the next call needs: the order_line_item_id, the fulfillment_id it shipped on, and a return_reason_id the buyer could pick. Flint worked out the remaining quantity in eligibility.eligible_quantity, so you never have to reconstruct what earlier Returns already used up.

With no policy in place you will see status: "review_required" and reason: "no_matching_policy" instead. The Return can still be created; it just waits for you to decide it. Return policies is how you make that automatic.

2

Take it back#

One call creates the Return, approves it, records that the item arrived, sends it back on sale, and starts the refund.

Bash
curl -X POST https://api.withflintpay.com/v1/returns/process \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: first-return-001" \
  -d '{
    "order_id": "ord_1kmn0aExample",
    "completion_behavior": "complete_when_ready",
    "receipt": {
      "receiving_location_id": "loc_1kmn0aExample",
      "source_system": { "source_system_type": "pos", "external_source_id": "store-1042" },
      "received_at": "2026-07-29T14:30:00Z"
    },
    "line_items": [
      {
        "order_line_item_id": "li_1kmn0aExample",
        "requested_quantity": 1,
        "return_reason_id": "rrsn_1kmn0aExample",
        "decision": {
          "decision_basis": "explicit",
          "approved_quantity": 1,
          "return_required_quantity": 1,
          "allowed_resolution_types": ["refund"],
          "selected_resolution_type": "refund",
          "resolution_mode": "automatic",
          "refund_timing": "after_receipt",
          "is_inspection_required": false,
          "receiving_location_id": "loc_1kmn0aExample"
        },
        "received_quantity": 1,
        "disposition": {
          "disposition_type": "sellable",
          "inventory_location_id": "loc_1kmn0aExample",
          "reason": "inspection_result",
          "occurred_at": "2026-07-29T14:30:00Z"
        },
        "resolution": { "resolution_type": "refund", "quantity": 1 }
      }
    ]
  }'
JSON
{
  "data": {
    "idempotency_key": "first-return-001",
    "return": {
      "return_id": "ret_1kmn0aExample",
      "return_number": "R-1001",
      "order_id": "ord_1kmn0aExample",
      "status": "open",
      "decision_status": "approved",
      "merchandise_status": "resolved",
      "resolution_status": "pending",
      "return_revision": 1,
      "completion_mode": "automatic",
      "completion_blockers": [
        { "code": "resolution_pending", "message": "Approved buyer value has not been resolved." }
      ]
    },
    "return_receipts": [{ "return_receipt_id": "retrc_1kmn0aExample" }],
    "return_inspections": [],
    "return_dispositions": [{ "return_disposition_id": "retdsp_1kmn0aExample" }],
    "return_resolutions": [{ "return_resolution_id": "retres_1kmn0aExample", "status": "pending" }]
  },
  "request_id": "req_1kmn0aExample"
}

The response is a fact graph, not a single object: the Return under return, and every child it just created alongside it. Nothing is nested inside the Return, so you get all the IDs in one read.

The 201 means those facts committed, not that the money has moved. The merchandise side is already resolved because the item arrived and went back on sale in the same call. The refund is still in flight, which is why one blocker remains.

Idempotency-Key is required here

/process moves stock and money, so it takes a caller-chosen key. If the response never arrives, replay the same key. Because you do not yet know the return_id, recover with GET /v1/returns?idempotency_key=first-return-001 rather than sending a second create.

3

Watch it settle#

Read the Return until completion_blockers empties out.

Bash
curl https://api.withflintpay.com/v1/returns/ret_1kmn0aExample \
  -H "Authorization: Bearer YOUR_API_KEY"
JSON
{
  "data": {
    "return_id": "ret_1kmn0aExample",
    "status": "completed",
    "decision_status": "approved",
    "merchandise_status": "resolved",
    "resolution_status": "fulfilled",
    "return_revision": 3,
    "completion_blockers": [],
    "financial_summary": {
      "returned_total_money": { "amount": 4500, "currency": "USD" },
      "refunded_money": { "amount": 4500, "currency": "USD" }
    }
  },
  "request_id": "req_1kmn0aExample"
}

In production you would not poll. Subscribe to return.completed and let Flint tell you. Set that up with the webhooks guide.

What that one call actually wrote#

/process is a shortcut through the same durable objects the stepwise routes create one at a time. Your Return now has all of them, and the response already handed you their IDs:

FactSent asCame back asAlso readable at
The request and its approvaldecisiondata.returnGET /v1/returns/{return_id}
The merchandise arrivingreceiptdata.return_receiptsGET /v1/return-receipts?return_id=...
Where the merchandise wentdispositiondata.return_dispositionsGET /v1/return-dispositions?return_id=...
What the buyer getsresolutiondata.return_resolutionsGET /v1/return-resolutions?return_id=...
The money itselfthe resolution's effectnot yet, it is asyncGET /v1/refunds?return_id=...

Nothing is hidden or collapsed. That matters the moment a real flow stops being atomic: a mail-in return records the receipt days after the decision, and a warehouse records it under a key that cannot refund.

Where to go next#

Rate this doc