Measura API documentation · v1

Create order return

Return fulfilled built-in measured amounts or whole copies of custom-unit selections with explicit restocking.

Code example
POST /api/v1/orders/{orderId}/returns

Headers:
  Authorization: Bearer <api-key>
  Content-Type: application/json
  Idempotency-Key: order-change-unique-key

Path parameters

Scroll horizontally to see all columns.

ParameterTypeDescription
orderIdstringRequired — Numeric Shopify order legacy (REST) ID; do not send a GraphQL GID.

Request body

Code example
{
  "expectedRevision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "lines": [
    {
      "lineItemId": "111",
      "returnMeasurement": {
        "type": "weight",
        "value": 0.25,
        "unit": "kg"
      },
      "restock": true
    }
  ],
  "notifyCustomer": false,
  "staffNote": "Return measured amount"
}

Response example (200 success)

Code example
{
  "success": true,
  "data": {
    "order": {
      "id": "1234567890",
      "name": "#1001",
      "currencyCode": "USD",
      "revision": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
      "updatedAt": "2026-10-19T00:01:00Z",
      "cancelledAt": null,
      "financialStatus": "PAID",
      "fulfillmentStatus": "FULFILLED",
      "items": [
        {
          "lineItemId": "112",
          "productId": "222",
          "variantId": "333",
          "quantity": 1,
          "measurement": {
            "type": "weight",
            "value": 1.0,
            "unit": "kg"
          },
          "unitPrice": "$10.00 per kg",
          "totalPrice": 10.0,
          "title": "Measured product",
          "originalLineItemId": "111",
          "previousLineItemId": "111",
          "originalProductId": "222",
          "originalVariantId": "333",
          "unfulfilledQuantity": 0,
          "fulfilledQuantity": 1,
          "editability": {
            "canEdit": false,
            "canSplit": false,
            "canAdjustFulfilled": true,
            "canReturn": true,
            "reason": "Use fulfilledEdits or measured returns for fulfilled lines."
          }
        }
      ],
      "totals": {
        "total": {
          "amount": "10.00",
          "currencyCode": "USD"
        },
        "tax": {
          "amount": "0.00",
          "currencyCode": "USD"
        },
        "shipping": {
          "amount": "0.00",
          "currencyCode": "USD"
        },
        "outstanding": {
          "amount": "0.00",
          "currencyCode": "USD"
        }
      },
      "paymentSettlement": "separate"
    },
    "lineMappings": [
      {
        "sourceLineItemId": "111",
        "originalLineItemId": "111",
        "resultingLineItemIds": [
          "112"
        ]
      }
    ],
    "restock": "original_fulfillment_location",
    "paymentSettlement": "separate",
    "operationId": "123e4567-e89b-42d3-a456-426614174000",
    "operationStatus": "completed"
  },
  "error": null
}

Response fields

Scroll horizontally to see all columns.

FieldTypeDescription
data.orderobjectFull current order state, using the Get order details schema.
data.lineMappingsarraysourceLineItemId, originalLineItemId, and resultingLineItemIds for previous measured lines.
data.restockstringoriginal_fulfillment_location for built-in returns; per_line for custom returns.
data.shopifyReturnIdstringOptional — Shopify return ID returned by the custom-unit engine, when available. This is not returnId for the public operation detail route.
data.operationId / operationStatusstringOperation UUID and completed status. Use operationId as returnId on the public return detail endpoint.
data.paymentSettlementstringAlways separate. Monetary refunds require separate settlement.

Error responses

Scroll horizontally to see all columns.

Status codeDescription
400bad_request — Invalid JSON, missing Idempotency-Key or expectedRevision, unsupported fields, invalid measurements or quantities, duplicate lines, or mixed workflows.
405method_not_allowed — Use GET or POST; Allow: GET, POST.
404not_found — Order not found or unavailable.
409conflict — Stale revision; reload the order and submit a new request with its new revision and a new idempotency key.
409idempotency_key_reused — The same key was used for a different method, resource, or request body.
409operation_in_progress / resource_in_progress — This operation or another operation on the order is still running. Retry-After: 2 may be returned.
409operation_requires_review — Changes may have applied, or receipt persistence is uncertain. Retrieve the operation and reconcile before another write.
422unprocessable_entity — Cancelled order, unavailable action, wrong line ownership or measurement workflow, or another unsupported order state.
401unauthorized — Missing or invalid API key.
403forbidden — The Order edit add-on is not ACTIVE for this shop.
500internal_error — The request could not be processed.

Note

  • Both writes and operation-history reads require the active Order edit add-on.
  • Idempotency-Key is required (1–255 characters, not blank). Keep one unique key per intended write across the shop. Retry an identical request with its original key after a lost response; a changed request needs a new key. Completed/rejected replays return the saved result and Idempotency-Replayed: true.
  • Only one operation may own an order at a time. Inspect operation_in_progress or resource_in_progress before retrying. An operation_requires_review outcome can include applied changes and keeps the order blocked until reconciled; never start the same mutation with a fresh key to bypass it.
  • Order changes and monetary settlement are separate. Handle payment collection or refunds separately.
  • Read the order first, use its latest revision, and choose lines with editability.canReturn. Built-in and custom return workflows must be submitted separately.
  • Built-in returnMeasurement.value is the total measured amount to return across the fulfilled line, not a per-copy amount. Use the sold measurement type and a valid positive measurement/unit. The engine validates the amount against the fulfilled selection.
  • Built-in lines require restock: true and use the original fulfillment location. Do not send inventoryLocationId or request no-restock for built-in returns.
  • Custom returns take whole-copy quantities from 1 to 9999. Set restock explicitly; restock: true requires a numeric legacy inventoryLocationId, while restock: false must omit inventoryLocationId.

Request fields

Scroll horizontally to see all columns.

FieldTypeDescription
expectedRevisionstringRequired — 64-character lowercase revision copied from a fresh GET order response.
linesarrayEither lines or customLines is required. 1–100 unique built-in line entries, each {lineItemId, returnMeasurement: {type, value, unit}, restock: true}.
customLinesarrayEither lines or customLines is required. 1–100 unique entries: {lineItemId, quantity, restock, inventoryLocationId?}. Return the whole historical custom selection per copy.
notifyCustomerbooleanOptional for built-in returns. Custom returns reject true.
staffNotestringOptional for built-in returns; maximum 2000 characters. Not supported for custom returns.

Custom-unit return without restocking

Code example
{
  "expectedRevision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "customLines": [
    {
      "lineItemId": "111",
      "quantity": 1,
      "restock": false
    }
  ]
}

Custom-unit return with restocking

Code example
{
  "expectedRevision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "customLines": [
    {
      "lineItemId": "111",
      "quantity": 1,
      "restock": true,
      "inventoryLocationId": "444"
    }
  ]
}