Create a return

A return sends units of an order back to the shop for a refund. This page walks the four calls of the flow: read the order, read the policy, list the shipping methods and create the return. Exchanges reuse the first three steps and have their own page, Create an exchange, with the payment of a price difference. All the calls need the session token and the API key described in Getting started; every identifier the calls take is a Shopify one, except the reason, the shipping method and the uploaded files, which come from the API itself, and the fulfillment, where the creation accepts the Shopify id or the Drelife one served by GET /orders. Examples are abridged to the fields the flow needs; the API reference has the complete shapes.

1. Read the order and its eligibility

GET /orders?lang=en

The response is a list: an order sold by a shop with child shops is served as one element per destination, a regular order as a single element.

[
  {
    "id": "5798222299429",
    "orderName": "#1001",
    "currency": "EUR",
    "creationDate": "2026-09-01T09:14:22.000Z",
    "customer": { "firstName": "Jane", "lastName": "Doe" },
    "address": { "address1": "Calle Mayor 1", "city": "Madrid", "zip": "28013", "countryCode": "ES", "provinceCode": "M" },
    "items": [
      {
        "id": "14577068736858",
        "fulfillmentId": "0c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
        "productId": "8253231888",
        "variantId": "47046763118885",
        "name": { "en": "Essentials slim T-shirt", "es": "Camiseta slim Essentials" },
        "description": { "en": "XS" },
        "price": 29.95,
        "quantity": 2,
        "quantityReturned": 0,
        "quantityAvailable": 2,
        "returnable": true,
        "cannotBeExchanged": false,
        "blacklisted": false,
        "outOfDateReturns": false,
        "outOfDateExchanges": false,
        "expiryDate": "2026-10-01T09:14:22.000Z",
        "maxReturnDays": 30,
        "maxExchangeDays": 30,
        "reasons": [
          {
            "id": "9e09f0f3-55bd-4be9-ab86-971eeb773ded",
            "type": "Return",
            "content": { "en": "Wrong size", "es": "Talla incorrecta" },
            "allowCustomerInfo": true,
            "requireCustomerInfo": false,
            "allowCustomerImage": true,
            "requireCustomerImage": false,
            "children": []
          },
          {
            "id": "6726de5d-1f4c-4912-9050-3a7123dad020",
            "type": "Change",
            "content": { "en": "I want another size", "es": "Quiero otra talla" },
            "children": []
          }
        ],
        "questions": []
      }
    ],
    "returns": [],
    "policy": { "maxReturnDays": 30, "maxExchangeDays": 30, "allowedPaymentMethods": ["CreditCard", "StoreCredit"] }
  }
]

Read the eligibility of each line item from its flags before offering anything to the shopper:

FieldMeaning
returnableThe item can be sent back for a refund. False when the shop marked it as not returnable, the shop is inactive or returns are disabled for this shopper
cannotBeExchangedThe item cannot be exchanged (same conditions, for exchanges)
blacklistedThe product is in the shop blacklist: neither return nor exchange
outOfDateReturns / outOfDateExchangesThe deadline of the item has passed: a date set by the shop for the fulfillment, or its delivery date plus the window of the policy. Use these flags; do not compute the deadline from creationDate
quantityAvailableUnits not yet returned. Zero means every unit is already in a return
reasonsThe reasons the shopper can pick for this item. type is Return (refund) or Change (exchange). A reason with children is a group: pick one of the children. requireCustomerInfo and requireCustomerImage tell you whether a comment or pictures are mandatory
questionsExtra questions the shop asks for this item; answer them in answers when creating the return. Pictures and videos are attached through the upload endpoints described in Attachments

An item is offerable as a refund when returnable is true, blacklisted is false, outOfDateReturns is false and quantityAvailable is greater than zero; as an exchange when cannotBeExchanged is false, blacklisted is false, outOfDateExchanges is false and quantityAvailable is greater than zero. The returns array lists the returns already made for the order.

Keep id (the line item), fulfillmentId (a Drelife id here; the Shopify fulfillment id is accepted as well) and the chosen reason id: the creation needs the three.

2. Read the policy

GET /policies?country=ES&province=M
{
  "mode": "Returns",
  "maxReturnDays": 30,
  "maxReturnDaysEnabled": true,
  "maxExchangeDays": 30,
  "maxExchangeDaysEnabled": true,
  "maxNumberOfItems": 5,
  "instantExchangesEnabled": false,
  "normalExchangesEnabled": true,
  "differentProductExchangeEnabled": true,
  "allowedPaymentMethods": ["CreditCard", "StoreCredit"],
  "storeCreditExtraQuantity": 5,
  "isStoreCreditExtraQuantityPercentage": true,
  "termsAndConditions": { "en": "...", "es": "..." },
  "explicitTermsAndConditions": true,
  "needValidation": false
}

Pass the country and province of the shipping address: shops can have different rules per region, and an unknown region falls back to the base policy. What matters for the flow:

  • allowedPaymentMethods: the refund methods the shopper can choose in the creation (CreditCard refunds to the original payment, StoreCredit gives a shop voucher, plus GiftCard and BankTransfer when enabled). storeCreditExtraQuantity is the bonus for choosing store credit.
  • maxNumberOfItems: units a single return can contain.
  • normalExchangesEnabled / instantExchangesEnabled: which kinds of exchange the shop offers (instantExchange in the creation must match one of them). When the exchange is dispatched depends on the shop configuration and on pending payment or validation.
  • needValidation: when true the shop validates every return before it can be shipped; see pre-validation.
  • termsAndConditions and explicitTermsAndConditions: show the terms to the shopper; the creation requires their acceptance.

3. List the shipping methods

Ask for the methods that can carry the items you are about to return; the price depends on the items, on the address of the order and on the shop rules (free shipping thresholds, free exchanges).

POST /carriers/methods
Content-Type: application/json

{
  "items": [
    {
      "id": "14577068736858",
      "fulfillmentId": "5294120468773",
      "items": [
        { "fulfillmentId": "5294120468773", "reasonId": "9e09f0f3-55bd-4be9-ab86-971eeb773ded" }
      ]
    }
  ]
}
[
  {
    "id": "fdba82c7-abd2-441f-9498-15227cfa0514",
    "shippingMethodId": "BNS_PCL",
    "shippingMethodType": "DropOff",
    "name": "Drop off at a parcel shop",
    "carrierName": "GLS",
    "price": 3.95,
    "currency": "EUR",
    "needAddress": true,
    "needSchedule": false
  },
  {
    "id": "0b6d2a4e-8f2c-4a1e-9c3d-7e1f2a3b4c5d",
    "shippingMethodId": "PCL_PICKUP",
    "shippingMethodType": "PickUp",
    "name": "Home pick up",
    "carrierName": "GLS",
    "price": 5.95,
    "currency": "EUR",
    "needAddress": true,
    "needSchedule": true,
    "pickUpDates": ["2026-09-11", "2026-09-14", "2026-09-15", "2026-09-16", "2026-09-17", "2026-09-18", "2026-09-21"]
  }
]

Every method carries two identifiers and both are needed in the creation: id (the method in Drelife, methodId there) and shippingMethodId (the product of the carrier). shippingMethodType is one of DropOff (the shopper takes the parcel to a point), PickUp (a courier collects it: needSchedule is true and a date is required), Shop (the shopper brings it to a physical shop), Merchant and Custom (the shop arranges the shipping itself). A price of 0 means the shipping is free for this return.

A PickUp method also lists pickUpDates: the days the courier can collect, as YYYY-MM-DD in order. They are working days (Monday to Friday) starting the day after tomorrow, seven of them for most carriers (FedEx Mexico offers the next working day only). They are counted from the localDate of the request (YYYY-MM-DD, the calendar day of the shopper; optional, the UTC day of the server otherwise), so a shopper whose day differs from the server's is offered the days they expect. desiredPickUp.date in the creation must be 00:00 UTC of one of them; any other day is rejected with PickUpDateTooSoon, PickUpDateNotWorkingDay or MaxPickUpDaysExceeded.

An empty list means no method serves the address of the order. A 502 CarrierUnavailable means no method could be priced and at least one carrier failed to answer: retry a bit later.

4. Create the return

Group the units by line item: one entry per id, with one element in items per unit being returned (two units of the same T-shirt are two elements, each with its own reason). fulfillmentId accepts the value of GET /orders or the Shopify fulfillment id.

POST /returns/create?lang=en
Content-Type: application/json

{
  "items": [
    {
      "id": "14577068736858",
      "fulfillmentId": "5294120468773",
      "items": [
        {
          "fulfillmentId": "5294120468773",
          "reasonId": "9e09f0f3-55bd-4be9-ab86-971eeb773ded",
          "customerInfo": "It is too small for me"
        }
      ]
    }
  ],
  "methodId": "fdba82c7-abd2-441f-9498-15227cfa0514",
  "shippingMethodId": "BNS_PCL",
  "shippingType": "DropOff",
  "paymentMethod": "CreditCard",
  "instantExchange": false,
  "termsAndConditionsAcceptance": true
}
FieldRules
items[].id, items[].fulfillmentIdThe line item and its fulfillment, from GET /orders
items[].items[]One element per unit: fulfillmentId and reasonId (a reason of type Return of that item) required; customerInfo (512 characters), customerImageNames (see Attachments) and answers optional
methodId, shippingMethodId, shippingTypeThe three values of the chosen method (id, shippingMethodId, shippingMethodType)
paymentMethodHow the shopper gets the money: CreditCard (the original payment), StoreCredit, GiftCard or BankTransfer, among allowedPaymentMethods of the policy (400 Invalid Payment Method otherwise)
instantExchangeAlways sent; false for a return
termsAndConditionsAcceptanceMust be true
desiredPickUpRequired for a PickUp method, see below

The shipping address and the contact of the return are those of the order; carriers need a phone number on it, so an order imported without one is refused with 400 PhoneIsRequired. Update the contact first with PATCH /orders/contact (phone, and optionally firstName, lastName, address1, address2, city, zip, province, provinceCode, countryCode, email) and create again.

The refund of a return is issued by the shop when it processes the parcel, never at creation: the API stores the method and the amount. The refund is the value of the units minus the return shipping when the shopper pays it; a return whose value does not cover the shipping is refused with 400 OrderAmountLowerThanShipping.

Pick ups

When the chosen method is a PickUp, add the date the courier should come, as a Unix timestamp in milliseconds of that day. Only the date counts; it must be from tomorrow up to seven calendar days ahead. Generate the value dynamically:

"desiredPickUp": { "date": 1789734400000 }

The response

{
  "id": "44891408138533",
  "name": "1001-R1",
  "state": "Pending",
  "creationDate": "2026-09-16T10:12:00.000Z",
  "currency": "EUR",
  "value": 29.95,
  "shippingCost": 3.95,
  "totalPrice": 29.95,
  "paymentMethod": "CreditCard",
  "shippingType": "DropOff",
  "needLabel": true,
  "labelURL": null,
  "qrCodeURL": null,
  "items": [
    {
      "id": "47046763118885",
      "name": "Essentials slim T-shirt",
      "price": 29.95,
      "status": "OnHold",
      "reason": { "id": "9e09f0f3-55bd-4be9-ab86-971eeb773ded", "content": { "en": "Wrong size" } }
    }
  ]
}

items[].id is the variant of the returned unit. value is the value of the returned items (totalPrice is a deprecated alias); the shipping cost is shippingCost. The instructions for the shopper and the tracking come with the detail, not with the creation response.

The creation is asynchronous: the label, the QR code and the tracking are generated afterwards, and their generation can wait for a payment or for the validation of the shop. When the method needs a document (needLabel is true), fetch the return again (see Follow a return) until labelURL or qrCodeURL is filled, with a bounded number of attempts spaced a few seconds apart. GET /returns/{id}/label answers 404 while neither document exists yet.

The creation is not idempotent. Do not retry a 201 you did not receive blindly: read the returns of the order first (GET /returns/history) to check whether it was created. A 405 OrderLocked means another creation for the same order is in progress: wait a few seconds and retry.

A return can also carry exchanged units; the balance and the payment that may follow are described in Create an exchange.

Pre-validation shops

When the policy says needValidation, use POST /returns/request instead of /returns/create, with only items and termsAndConditionsAcceptance: the shop reviews the request first, and the shopper completes shipping and refund method afterwards. The whole flow is described in Pre-validation.


Did this page help you?