POST /orders/{orderId}/change-requests

Report lines the seller cannot ship as sold, before the order ships. Each line is removed, or substituted whole with another SKU of the same catalog product that the seller lists, at a per-unit credit. The buyer answers the report: they accept it, have every affected line removed instead, or cancel the order. Snapcaster emails the buyer, opens the order's support conversation if none is open, and sends order.change_request.created. When the buyer answers, order.change_request.resolved follows with the outcome; an accepted substitution sends order.updated with reason: line_substituted, and removed units send order.cancelled. Returns the pending report. In the sandbox, list two of the sandbox catalog SKUs, buy one in a test purchase, and name the other as substituteSkuId; the example substitutes SKU 363348 for 14602.

Example

These examples send requests to the sandbox.

Replace the example orderId in the URL with your own.

cURL request - Sandbox: Substitute a whole line with a credit
curl --request POST \
  --url 'https://api-sandbox.snapcaster.ca/api/v1/marketplace/orders/ord_sandbox_01K4A8Y6D4QJH2N3P5R7S9T0VW/change-requests' \
  --header "Authorization: Bearer $MARKETPLACE_API_KEY" \
  --header "Idempotency-Key: $(uuidgen)" \
  --header 'Content-Type: application/json' \
  --data '{
  "kind": "fulfillment_report",
  "payload": {
    "lines": [
      {
        "lineRef": "oln_1f0c8a4d7e2b45c9a1d6f3b8e07c9241",
        "action": "substitute",
        "substituteSkuId": 363348,
        "creditCents": 100
      }
    ],
    "note": "Our near mint copy was damaged; we can send a lightly played one."
  }
}'
cURL request - Sandbox: Remove one unit
curl --request POST \
  --url 'https://api-sandbox.snapcaster.ca/api/v1/marketplace/orders/ord_sandbox_01K4A8Y6D4QJH2N3P5R7S9T0VW/change-requests' \
  --header "Authorization: Bearer $MARKETPLACE_API_KEY" \
  --header "Idempotency-Key: $(uuidgen)" \
  --header 'Content-Type: application/json' \
  --data '{
  "kind": "fulfillment_report",
  "payload": {
    "lines": [
      {
        "lineRef": "oln_1f0c8a4d7e2b45c9a1d6f3b8e07c9241",
        "action": "remove",
        "quantity": 1
      }
    ]
  }
}'

$(uuidgen) makes a new Idempotency-Key each time you run the command. To retry a command after a lost response, send the key from the first attempt.

Response 201: Substitution with a credit
{
  "success": true,
  "data": {
    "id": 382,
    "order_id": "ord_sandbox_01K4A8Y6D4QJH2N3P5R7S9T0VW",
    "kind": "fulfillment_report",
    "status": "pending",
    "proposed_by_party": "seller",
    "created_at": "2026-09-04T19:01:12.000Z",
    "resolved_at": null,
    "lines": [
      {
        "line_ref": "oln_1f0c8a4d7e2b45c9a1d6f3b8e07c9241",
        "name": "Abyssal Nightstalker",
        "quantity": 2,
        "sku_id": 14602,
        "product_id": 86,
        "ordered": {
          "name": "Abyssal Nightstalker",
          "set": "Portal Second Age",
          "language": "EN",
          "finish": "Normal",
          "rarity": "U",
          "condition": "NM",
          "collector_number": null
        },
        "action": "substitute",
        "substitute": {
          "listing_id": 5021,
          "sku_id": 363348,
          "product_id": 86,
          "external_ref": "platform-listing-1043",
          "name": "Abyssal Nightstalker",
          "set": "Portal Second Age",
          "language": "EN",
          "finish": "Normal",
          "rarity": "U",
          "condition": "LP",
          "collector_number": null,
          "printing": "Normal"
        },
        "credit_cents": 100,
        "dispositions": null
      }
    ],
    "amount_cents": 233,
    "note": "Our near mint copy was damaged; we can send a lightly played one.",
    "credits": {
      "accept_cents": 233,
      "remove_instead_cents": 1165,
      "cancel_order_cents": 2039
    },
    "address": null,
    "previous_address": null
  }
}

Endpoint

EnvironmentURL
Sandboxhttps://api-sandbox.snapcaster.ca/api/v1/marketplace/orders/{orderId}/change-requests
Productionhttps://api.snapcaster.ca/api/v1/marketplace/orders/{orderId}/change-requests
Authentication
  • marketplaceApiKey - http bearer. Send the seller's marketplace API key as Authorization: Bearer mp_{sellerCode}_{hex}. The mp_{sellerCode}_ prefix identifies the seller; the trailing hex is the secret. The seller generates the key and supplies it to your platform.

Request

Path parameters

orderId string required

The opaque public order id (the webhook/poll order_id).

Constraints: minimum length: 1 Example ord_sandbox_01K4A8Y6D4QJH2N3P5R7S9T0VW

Headers

Idempotency-Key string required

Keep this UUID when retrying the same order operation and body. Keys remain valid for the lifetime of the order. Changed contents return 409 IDEMPOTENCY_CONFLICT; a concurrent attempt returns 409 REQUEST_IN_PROGRESS with Retry-After.

Constraints: format: uuid Example f04e4a96-3c99-44dc-abce-5b6862c589c9

X-Snapcaster-Api-Version string

Override the platform pin for this request only. Webhook versions and the platform pin do not change.

Constraints: allowed values: 2026-10 Example 2026-10

Request body

kind string required

The seller reports that lines cannot ship as sold. It is the only kind a seller files.

Constraints: allowed values: fulfillment_report Example fulfillment_report

payload object required

Constraints: additional properties are not allowed

Show child fieldsHide child fields
payload.lines array of object required

The lines that cannot ship as sold, each named once by lineRef or skuId. A report that removes every remaining unit is refused: cancel the order with POST /orders/{orderId}/cancel instead.

Constraints: minimum items: 1

Show child fieldsHide child fields
payload.lines[].lineRef string

The purchase-line ref (oln_…) of the order line this entry names, read from the order's own lines. Name a line by lineRef or by skuId; lineRef is needed only when the SKU is on more than one line the command can still act on.

Constraints: pattern: ^oln_[0-9a-f]{32}$ Example oln_1f0c8a4d7e2b45c9a1d6f3b8e07c9241

payload.lines[].skuId integer

The SKU of the order line this entry names. Name a line by skuId or by lineRef. When the SKU is on more than one line the command can still act on, the command answers ORDER_LINE_AMBIGUOUS with the candidate lineRefs; name the line by one of them. Supply exactly one of lineRef and skuId.

Constraints: exclusive minimum: 0

payload.lines[].quantity integer

How many units to remove. Omit it to remove the line's remaining units.

Constraints: exclusive minimum: 0

payload.lines[].action string required

Remove the units from the order.

Constraints: allowed values: remove, substitute Example substitute

payload.lines[].substituteSkuId integer conditional

Required when action is substitute. The SKU to ship instead. It must be another SKU of the line's catalog product that the seller lists live, with stock for the whole line, and not a SKU already on the order or in this report. Snapcaster reads the product from its catalog, never from the listing. GET /orders/{orderId}/substitute-candidates lists the SKUs that qualify.

Constraints: exclusive minimum: 0 Example 363348

payload.lines[].creditCents integer conditional

Required when action is substitute. The credit per unit refunded to the buyer if they accept, in cents. Between 0 and the line's unit_price less any substitution credit already returned on it.

Constraints: minimum: 0 Example 100

payload.note string

A short note to the buyer, shown with the report.

Constraints: minimum length: 1; maximum length: 500 Example Our near mint copy was damaged; we can send a lightly played one.

message string

A message for the buyer, posted in the order's support conversation, which the report opens if none is open.

Constraints: minimum length: 1; maximum length: 10000

Response

201

The report, pending until the buyer answers it.

Response body: ChangeRequestResponse

success boolean required

Whether the operation completed as requested.

Constraints: allowed values: true Example true

data ChangeRequest required

The operation or event payload.

Show child fieldsHide child fields
data.id integer required

The change request id, stable for the life of the request and the {requestId} of its answers (STABLE).

Constraints: exclusive minimum: 0 Example 382

data.order_id string required

The order the request is about (STABLE).

Example ord_sandbox_01K4A8Y6D4QJH2N3P5R7S9T0VW

data.kind string required

address_change: the buyer asks to ship to address instead of previous_address. cancel_lines: the buyer asks to remove lines, or every remaining line to end the order. fulfillment_report: the seller reports that lines cannot ship as sold, and the buyer answers it (STABLE).

Constraints: allowed values: cancel_lines, fulfillment_report, address_change Example fulfillment_report

data.status string required

pending until it is answered; a request has no deadline. accepted: the change ran. declined: the answering party refused it and nothing changed. withdrawn: the party who raised it took it back. voided: the order shipped, was cancelled or refunded, its address was corrected, or Snapcaster closed the support conversation the request belonged to, before anyone answered. removed_instead and order_cancelled are the buyer answers to a fulfillment_report that remove every affected line or end the order. expired appears only on requests from before requests stopped lapsing (STABLE).

Constraints: allowed values: pending, accepted, removed_instead, order_cancelled, declined, withdrawn, voided, expired Example pending

data.proposed_by_party string required

Who raised it. The seller answers a buyer request by accepting or declining it; the buyer answers a seller request (STABLE).

Constraints: allowed values: buyer, seller, operator, system Example seller

data.created_at string required

When the request was raised (STABLE).

Constraints: format: date-time Example 2026-09-04T19:01:12.000Z

data.resolved_at string or null required

When the request left pending; null while it is pending (STABLE).

Constraints: format: date-time Example null

data.lines ChangeRequestLine[] or null required

The lines a cancel_lines or fulfillment_report request names; null on an address_change (STABLE).

Show child fieldsHide child fields
data.lines[].line_ref string required

The purchase line the request names, the same line_ref the order carries (STABLE).

Example oln_1f0c8a4d7e2b45c9a1d6f3b8e07c9241

data.lines[].name string required

The line title, or SKU <sku_id> when the catalog had none (STABLE).

Example Abyssal Nightstalker

data.lines[].quantity integer required

How many units of the line the request covers (STABLE).

Constraints: exclusive minimum: 0 Example 2

data.lines[].sku_id integer required

The SKU the line was sold as (STABLE).

Example 14602

data.lines[].product_id integer or null required

The product that SKU belongs to; null on a request recorded before it was kept (STABLE).

Example 86

data.lines[].ordered object required

The card the buyer ordered, as the request recorded it. A fact the request never recorded is null (STABLE).

Show child fieldsHide child fields
data.lines[].ordered.name string or null required

The card title (STABLE).

Example Abyssal Nightstalker

data.lines[].ordered.set string or null required

The set the printing belongs to; null when it was never recorded (STABLE).

Example Portal Second Age

data.lines[].ordered.language string or null required

The printing language; null when it was never recorded (STABLE).

Example EN

data.lines[].ordered.finish string or null required

The printing or finish, such as Foil; null when never recorded (STABLE).

Example Normal

data.lines[].ordered.rarity string or null required

The rarity; null when it was never recorded (STABLE).

Example U

data.lines[].ordered.condition string or null required

The condition grade; null when it was never recorded (STABLE).

Example NM

data.lines[].ordered.collector_number string or null required

The collector number as the catalog states it, such as 161/302; null when never recorded (STABLE).

Example null

data.lines[].action string required

remove on every cancel_lines line and on a report line that removes units. substitute on a report line that ships a replacement named in substitute at a per-unit credit_cents. split on a report line whose units go several ways, listed in dispositions (STABLE).

Constraints: allowed values: remove, substitute, split Example substitute

data.lines[].substitute ChangeRequestSubstitute or null required

The replacement on a substitute line; null on every other line (STABLE).

Show child fieldsHide child fields
data.lines[].substitute.listing_id integer required

Snapcaster's id for the seller listing offered in place of the line (STABLE).

Example 5021

data.lines[].substitute.sku_id integer required

The SKU the line ships as once the report is accepted (STABLE).

Example 363348

data.lines[].substitute.product_id integer required

The product, the same one the replaced line belongs to (STABLE).

Example 86

data.lines[].substitute.external_ref string or null required

Your platform's reference on that listing when the report was filed; null when none was sent (STABLE).

Example platform-listing-1043

data.lines[].substitute.name string or null required

The card title (STABLE).

Example Abyssal Nightstalker

data.lines[].substitute.set string or null required

The set the printing belongs to; null when it was never recorded (STABLE).

Example Portal Second Age

data.lines[].substitute.language string or null required

The printing language; null when it was never recorded (STABLE).

Example EN

data.lines[].substitute.finish string or null required

The printing or finish, such as Foil; null when never recorded (STABLE).

Example Normal

data.lines[].substitute.rarity string or null required

The rarity; null when it was never recorded (STABLE).

Example U

data.lines[].substitute.condition string or null required

The condition grade; null when it was never recorded (STABLE).

Example LP

data.lines[].substitute.collector_number string or null required

The collector number as the catalog states it, such as 161/302; null when never recorded (STABLE).

Example null

data.lines[].substitute.printing string or null required

The same fact as finish, under the catalog name (STABLE).

Example Normal

data.lines[].credit_cents integer or null required

The credit per unit on a substitute line, in cents; null on every other line (STABLE).

Example 100

data.lines[].dispositions array of object or null required

On a split line, each portion in the order the seller listed them; null on every other line (STABLE).

Show child fieldsHide child fields
data.lines[].dispositions[].action string required

What happens to this portion (STABLE).

Constraints: allowed values: remove, substitute

data.lines[].dispositions[].quantity integer required

How many of the line units go this way (STABLE).

Constraints: exclusive minimum: 0

data.lines[].dispositions[].substitute ChangeRequestSubstitute or null required

The replacement on a substitute line; null on every other line (STABLE).

Show child fieldsHide child fields
data.lines[].dispositions[].substitute.listing_id integer required

Snapcaster's id for the seller listing offered in place of the line (STABLE).

data.lines[].dispositions[].substitute.sku_id integer required

The SKU the line ships as once the report is accepted (STABLE).

data.lines[].dispositions[].substitute.product_id integer required

The product, the same one the replaced line belongs to (STABLE).

data.lines[].dispositions[].substitute.external_ref string or null required

Your platform's reference on that listing when the report was filed; null when none was sent (STABLE).

data.lines[].dispositions[].substitute.name string or null required

The card title (STABLE).

data.lines[].dispositions[].substitute.set string or null required

The set the printing belongs to; null when it was never recorded (STABLE).

data.lines[].dispositions[].substitute.language string or null required

The printing language; null when it was never recorded (STABLE).

data.lines[].dispositions[].substitute.finish string or null required

The printing or finish, such as Foil; null when never recorded (STABLE).

data.lines[].dispositions[].substitute.rarity string or null required

The rarity; null when it was never recorded (STABLE).

data.lines[].dispositions[].substitute.condition string or null required

The condition grade; null when it was never recorded (STABLE).

data.lines[].dispositions[].substitute.collector_number string or null required

The collector number as the catalog states it, such as 161/302; null when never recorded (STABLE).

data.lines[].dispositions[].substitute.printing string or null required

The same fact as finish, under the catalog name (STABLE).

data.lines[].dispositions[].credit_cents integer or null required

The credit per unit for a substitute portion; null for a removal (STABLE).

data.amount_cents integer or null required

The refund, tax included, that accepting a cancel_lines request returns to the buyer, or the refund a fulfillment_report returns when the buyer accepts it. Fixed when the request was raised. Null on an address_change and on an order with no payment (STABLE).

Example 233

data.note string or null required

The seller's note to the buyer on a fulfillment_report; null when none was given and on every other kind (STABLE).

Example Our near mint copy was damaged; we can send a lightly played one.

data.credits object or null required

The refund behind each buyer answer to a fulfillment_report, fixed when it was raised; null on every other kind and on an order with no payment (STABLE).

Show child fieldsHide child fields
data.credits.accept_cents integer required

The refund if the buyer accepts the report; repeats amount_cents (STABLE).

Example 233

data.credits.remove_instead_cents integer required

The refund if the buyer has every affected line removed instead (STABLE).

Example 1165

data.credits.cancel_order_cents integer required

The refund if the buyer ends the whole order instead (STABLE).

Example 2039

data.address OrderWebhookShippingAddress or null required

The shipping address an address_change asks for. Set only on an address_change; null on every other kind (STABLE).

Show child fieldsHide child fields
data.address.line1 string required

The street address.

data.address.line2 string or null required

The unit, suite, or second address line, if supplied.

data.address.city string required

The city in the shipping address.

data.address.province string required

The Canadian province or territory in the delivery address.

data.address.postal_code string required

The Canadian postal code in the delivery address.

data.address.country string required

ISO 3166-1 alpha-2 country code.

data.previous_address OrderWebhookShippingAddress or null required

The shipping address the order had when the address_change was raised. Set only on an address_change; null on every other kind (STABLE).

Show child fieldsHide child fields
data.previous_address.line1 string required

The street address.

data.previous_address.line2 string or null required

The unit, suite, or second address line, if supplied.

data.previous_address.city string required

The city in the shipping address.

data.previous_address.province string required

The Canadian province or territory in the delivery address.

data.previous_address.postal_code string required

The Canadian postal code in the delivery address.

data.previous_address.country string required

ISO 3166-1 alpha-2 country code.

Errors

Errors return a MarketplaceError body, except HTTP 413, 500 (ApiError). Error codes explains both error bodies and lists every code.

  • 400 INVALID_REQUEST IDEMPOTENCY_KEY_REQUIRED ORDER_LINE_AMBIGUOUS UNSUPPORTED_API_VERSION

    Invalid command body, missing/invalid UUID Idempotency-Key, or a skuId on more than one line the command can still act on (ORDER_LINE_AMBIGUOUS, with the candidate lineRefs in details). Unknown or retired API-version override (UNSUPPORTED_API_VERSION).

    Do this: Correct the request fields and supply a UUID Idempotency-Key. For ORDER_LINE_AMBIGUOUS, name that line by one of the lineRef values in details. Keep the same key when retrying the same command. For UNSUPPORTED_API_VERSION, choose a supported version from the API versioning guide.

  • 401 UNAUTHORIZED

    Missing or invalid marketplace API key (UNAUTHORIZED).

    Do this: Send the marketplace API key for this seller as a Bearer token, then retry.

  • 403 SUBSCRIPTION_EXCLUDES_ORDERS

    The platform's subscription is inventory, which does not include orders (SUBSCRIPTION_EXCLUDES_ORDERS). Only orders and both subscriptions read and act on orders.

    Do this: Do not retry. Ask Snapcaster to change the platform subscription to orders or both if the platform should handle orders.

  • 404 ORDER_NOT_FOUND

    No order matches this id for the authenticated seller (ORDER_NOT_FOUND). Another seller's order also returns 404, so the response does not reveal whether it exists.

    Do this: Check the order_id against GET /orders for the authenticated seller, then retry with that id.

  • 409 IDEMPOTENCY_CONFLICT REQUEST_IN_PROGRESS ORDER_CHANGE_REQUEST_ALREADY_PENDING SUPPORT_CASE_CLOSED

    IDEMPOTENCY_CONFLICT or REQUEST_IN_PROGRESS. Retry an in-progress command with the same key after Retry-After seconds. ORDER_CHANGE_REQUEST_ALREADY_PENDING when the order already has a pending change request, from either party; details carries its id. SUPPORT_CASE_CLOSED when Snapcaster closed the order's support conversation while the report was being filed.

    Do this: For REQUEST_IN_PROGRESS, wait Retry-After seconds and retry the same key and body. For IDEMPOTENCY_CONFLICT, reuse the original body or create a new UUID for a different operation. For ORDER_CHANGE_REQUEST_ALREADY_PENDING, read GET /orders/{orderId}/change-requests and answer or withdraw the pending request first. For SUPPORT_CASE_CLOSED, file the report again with a new key; it opens a new conversation.

  • 413 PAYLOAD_TOO_LARGE

    The body is larger than this route accepts (PAYLOAD_TOO_LARGE).

    Do this: Send a smaller body and retry.

  • 422 ORDER_CHANGE_REQUEST_NOT_ALLOWED INVALID_TRANSITION SUPPORT_CASE_WINDOW_CLOSED

    The report cannot be filed, with the codes the seller dashboard returns. ORDER_CHANGE_REQUEST_NOT_ALLOWED: the order has shipped or ended, or has no buyer to answer; the removals cover every remaining unit; or a substitute fails its rule (not a SKU the seller lists live, another catalog product, the line's own SKU or another SKU on the order, too little stock, a credit above what the line is worth, or a quantity smaller than the line's remaining units). INVALID_TRANSITION: a line has nothing left, or a quantity exceeds what it has left. SUPPORT_CASE_WINDOW_CLOSED: the order is too old for a support conversation, which every report opens or joins.

    Do this: Do not retry the same body. Read the message, then fix the line it names, pick a substitute from GET /orders/{orderId}/substitute-candidates, or cancel or remove the units instead.

  • 500 INTERNAL_ERROR

    An unexpected failure (INTERNAL_ERROR).

    Do this: Retry with bounded retries and jitter. If the failure persists, contact Snapcaster with the X-Correlation-ID response header.