> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sideshift.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Orders API

> Submit confirmed purchases and cumulative refunds from your server.

This endpoint is for connected custom stores. Authenticate with that store's **Orders API key**, not your platform API key, browser pixel code, or OAuth token. Keep the key in a server secret manager. Rotating it invalidates the old key; update your server configuration immediately after rotation.

Only call after verifying payment or a refund with your order system. Do not trust a browser's claimed payment status or amount. There is no dry-run parameter: a test order can affect commissions unless isolated or marked `isSample: true`.

Download the [OpenAPI specification](https://app.sideshift.app/openapi/commerce-orders.yaml) for client generation. It uses the same store-scoped authentication described here.

## Submit or update a verified order

```http theme={"system"}
POST https://app.sideshift.app/api/commerce/orders
Authorization: Bearer YOUR_ORDERS_API_KEY
Content-Type: application/json
```

```json theme={"system"}
{
  "orderId": "order-1042",
  "visitorId": "8ac8d41e-8e6e-433c-bec7-19c4e027f2e7",
  "totalCents": 6500,
  "refundedCents": 0,
  "currency": "USD",
  "occurredAt": "2026-09-11T12:30:00Z",
  "status": "paid",
  "items": [
    {
      "productId": "shirt-blue",
      "title": "Blue shirt",
      "quantity": 2,
      "priceCents": 3250
    }
  ]
}
```

| Field | Required | Meaning |
| - | - | - |
| `orderId` | Yes | Stable, unique order ID within this store. |
| `visitorId` | Yes | Visitor ID captured at checkout. |
| `totalCents` | Yes | Original order total in the currency's minor unit. |
| `currency` | Yes | Supported uppercase ISO currency code. |
| `occurredAt` | Yes | Payment time as an ISO 8601 UTC timestamp. |
| `refundedCents` | No | Cumulative refunded amount. Defaults to zero. |
| `status` | No | `paid`, `partially_refunded`, `refunded`, or `cancelled`. Defaults to `paid`. |
| `isSample` | No | Set to `true` for a sample order. Samples do not earn sales commission. |
| `items` | No | Product IDs, quantities, and unit prices. |

Despite their names, the `*Cents` fields always use the currency's **minor unit**: USD 65.00 is `6500`; JPY 65 is `65`; KWD 65.000 is `65000`. Send integers. Pixel display values use major units and are not the source of payable money.

The original total, currency, payment time, and existing visitor/checkout identity cannot change for the same order ID. Send cumulative refund amounts when an order is refunded. Do not send a new order ID for a refund.

### Refunds and commission

The store's **Deduct refunds** setting controls whether refunds reduce commission. When deductions apply, a percentage commission uses the remaining order value. A fixed commission is reduced in proportion to the refund: a 25% refund reduces a $4 commission by $1. A full refund reverses the full commission. A cancellation does not earn commission.

Find this setting in the store's **Tracking** tab. Changes apply to new commission calculations. Previously calculated commissions keep their refund rules.

If commission has already been paid, the reversal becomes an adjustment to a later payout. An order involved in a payout in progress is retried after that payout settles. The order keeps its original commission terms and exchange-rate snapshot so a replay does not change its calculation.

### Node.js example

```javascript theme={"system"}
async function submitSideShiftOrder(order) {
  const response = await fetch('https://app.sideshift.app/api/commerce/orders', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.SIDESHIFT_ORDERS_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      orderId: order.id,
      visitorId: order.sideShiftVisitorId,
      totalCents: order.originalTotalMinor,
      refundedCents: order.refundedTotalMinor,
      currency: order.currency,
      occurredAt: order.paidAt.toISOString(),
      status: order.status
    })
  });

  if (response.status === 429 || response.status >= 500) {
    // Schedule a retry with the same order ID and payload in your job queue.
    const retryAfter = Number(response.headers.get('Retry-After')) || 60;
    throw new Error(`Retry SideShift order submission after ${retryAfter} seconds`);
  }
  const result = await response.json();
  if (!response.ok) throw new Error(result.error);
  return result;
}
```

### Responses and retries

A successful response is HTTP `200`:

```json theme={"system"}
{ "accepted": true, "duplicate": false, "orderId": "SIDESHIFT_ORDER_ID" }
```

Replaying or updating an existing order returns HTTP `200` with `duplicate: true`. This means the order already existed, not that an incoming refund was ignored. It does not create another order. A changed total or currency for an existing ID returns HTTP `409`. A valid cumulative refund updates the existing order.

| Status | Action |
| - | - |
| `400` | Correct the request fields. |
| `401` | Check or replace the Orders API key. |
| `404` | Store tracking is not enabled for this deployment. |
| `409` | Check the order ID, original amount, currency, payment time, and checkout identity. |
| `413` | Reduce the request body below 256 KiB. |
| `429` | Retry after the `Retry-After` delay. |
| `500` / `502` | Retry with the same order ID. |

Acceptance confirms storage. It does not guarantee attribution or payout. A qualifying creator click, eligible commission terms, and verified order data are also required.

## Request limits

| Field or limit | Contract |
| - | - |
| Request body | At most 256 KiB JSON. |
| Rate | 600 requests per minute per store. Honor `Retry-After` on HTTP 429. |
| `orderId`, item `productId` | Nonempty strings, at most 200 characters after trimming. |
| `visitorId` | Required for this endpoint; 16–128 letters, numbers, underscores, or hyphens. |
| Money fields | Integers from 0 to 100,000,000,000 in currency minor units. |
| `occurredAt` | Original payment timestamp in UTC ISO 8601 format; at most five minutes in the future. |
| `items` | At most 500; each requires `productId`, integer `quantity` from 1 to 10,000, and `priceCents`. Optional `title` is at most 250 characters. |
| `checkoutToken` | Optional checkout correlation string, 16–200 characters. Keep stable on retries. |
| `advertising` | Optional context from the pixel. See [advertising measurement](/pixel/advertising). |

`refundedCents` must not exceed `totalCents`; `status: "refunded"` requires the full original total as `refundedCents`. Refund amounts are cumulative and never decrease. Cancellation and sample status cannot be undone by replaying an older paid payload. Keep your authoritative order state in sync before retrying.

## Refund example

Send the same original purchase identity and payment timestamp, updating cumulative refunds:

```json theme={"system"}
{
  "orderId": "order-1042",
  "visitorId": "8ac8d41e-8e6e-433c-bec7-19c4e027f2e7",
  "totalCents": 6500,
  "refundedCents": 1625,
  "currency": "USD",
  "occurredAt": "2026-09-11T12:30:00Z",
  "status": "partially_refunded"
}
```

Retry network failures, 429, and transient 5xx responses with exponential backoff and jitter in a durable server queue. Keep the same order ID, original purchase fields, and current cumulative refund state. Inspect other 4xx errors instead of retrying indefinitely. Error bodies expose an `error` message.

The response's `orderId` is SideShift's internal order ID. It can be `null` when a privacy-redacted order is suppressed; acceptance does not recreate deleted data. HTTP 200 does not confirm a matching creator click, commission payment, or delivery to an advertising network.
