# Orders and returns (outcomes)

> Send orders and returns so TryYourSize can report size-related returns per A/B arm. No customer data.

Source: https://docs.tryyoursize.com/guides/outcomes/

Orders and returns power the size-return report in **Studio → Results** and the [A/B holdout](https://docs.tryyoursize.com/guides/ab-holdout.md). Send them from your server, upload a CSV, or let the Shopify app read them.

> **Note: No customer data**
>
> There is no field for a customer ID, name, email, phone or address, and unknown fields are refused (`400`), so a stray `email` is rejected rather than stored. Free-text return notes are never accepted.

## Orders: `POST /v1/orders`

Secret key, or a restricted key with `orders:write`. Idempotent by your order ID: sending the same `id` again updates the order (`200`); a new order is `201`.

**Node.js**

```ts
await tys.orders.create({
  id: "1001",
  ordered_at: "2026-10-25T10:12:00Z",
  currency: "EUR",
  visitor_id: cart.tysVisitorId,               // from tys.experiment(), for the A/B arm
  lines: [
    {
      id: "1001-1",
      product: "STRAIGHT-JEANS",               // the ID you pass to the SDK
      size: "32",                               // size bought, in your labels
      quantity: 1,
      unit_price_minor: 8900,                   // optional, for the savings estimate
      tys_shown: true,
      recommended_size: "32",                   // from the recommendation event or _tys_size
      attribution: "sdk",
    },
  ],
});
```

**Python**

```python
tys.orders.create(
    id="1001",
    ordered_at="2026-10-25T10:12:00Z",
    currency="EUR",
    visitor_id=cart["tys_visitor_id"],
    lines=[{
        "id": "1001-1",
        "product": "STRAIGHT-JEANS",
        "size": "32",
        "quantity": 1,
        "tys_shown": True,
        "recommended_size": "32",
        "attribution": "sdk",
    }],
)
```

| Line field | |
|---|---|
| `id`, `product`, `size`, `quantity` | Required. `size` is the size bought, in your labels. |
| `variant`, `unit_price_minor` | Optional. Price in minor units (`5995` = 59.95). |
| `tys_shown` | A TryYourSize element was shown for this purchase. |
| `recommended_size` | The size TryYourSize recommended (the SDK's `recommendation` or `size` event, the server API, or the `_tys_size` cart line property). Whether the shopper followed it ("Medium" = "M") is computed for you. |
| `attribution` | Where `recommended_size` came from: `cart_attribute`, `sdk` or `api`. |

Order-level: `id`, `ordered_at`, `lines` (required), `currency`, and `visitor_id` or `experiment_arm` for the A/B arm. The `tryon_id` from [server-side recommendations](https://docs.tryyoursize.com/guides/server-side-recommendations.md) identifies the try-on behind a size; keep it with your order line for your own records.

## Returns: `POST /v1/returns`

Secret key, or a restricted key with `returns:write` (for example a returns platform). Idempotent by (line, return ID). The order must have been sent first (`404` otherwise).

```json
{
  "id": "RMA-5521",
  "order_id": "1001",
  "line_id": "1001-1",
  "quantity": 1,
  "reason": "size_too_small",
  "exchange_size": "33",
  "returned_at": "2026-11-02T09:00:00Z"
}
```

### Return-reason taxonomy

| Code | Size-related |
|---|---|
| `size_too_small` | yes |
| `size_too_large` | yes |
| `fit_other` (a zone, the length, "doesn't fit") | yes |
| `style` | no |
| `quality` | no |
| `damaged_or_wrong` | no |
| `changed_mind` | no |
| `other` | no |
| empty: `unspecified` | no |

Shopify's return reasons (`SIZE_TOO_SMALL`, `UNWANTED`, `DEFECTIVE`…) and common returns-platform words are mapped for you. `exchange: true` (or an `exchange_size`) marks an exchange instead of a refund.

## CSV import

No API integration? Upload CSV files in **Studio → Results → Import**.

- **Orders**, one row per line: `order_id, ordered_at, line_id, product, size, quantity` (optional: `currency, unit_price, variant, recommended_size, tys_shown, experiment_arm, visitor_id`).
- **Returns**: `order_id, line_id, return_id, quantity, returned_at` (optional: `reason, exchange, exchange_size`).
- Up to 10 MB or 50,000 rows. Rows with errors are skipped and listed by row number.
- **A file with a personal-data column** (`email`, `name`, `phone`, `address`, `customer…`, `ip`, `birth`…) **is refused as a whole.**

## Shopify

With the [Shopify app](https://docs.tryyoursize.com/quickstarts/shopify.md), grant the optional `read_orders` and `read_returns` permissions and TryYourSize reads orders, refunds and returns as they happen, and can import the last 1–60 days (Studio → Results → Import from Shopify). Only the fields the report needs are read: line items, the `_tys_size` and `_tys_vid` line properties, refunds and returns with their reasons. Never customer, address, email or note fields. Test orders, and orders of a store that is not live yet, are recorded in test mode.

## Attribution summary

| Source | Field |
|---|---|
| SDK `recommendation` / `size` event, or server-side recommendation | `recommended_size` with `attribution: "sdk"` or `"api"` |
| Shopify cart line property `_tys_size` | `recommended_size` with `attribution: "cart_attribute"` (automatic) |
| A/B arm | `visitor_id` (from `tys.experiment()`, or `_tys_vid` on Shopify), else `experiment_arm` |
