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

# Migrate from V1

> Guide for migrating from the Sync API to the Universal Checkout API.

Migration is organized into three phases — do them in order, but each phase can be deployed independently:

1. **Product catalog** — how you discover and keep product data fresh
2. **Ordering** — how you place a purchase
3. **Post-purchase** — tracking, webhooks, returns

Migrating can be done in under a week of concerted effort, thanks to the simpler API surface of the Universal Checkout API.

## At-a-glance

| | V1 — Sync API | V2 — Universal Checkout |
| - | - | - |
| Protocol | GraphQL, single endpoint | REST, resource-per-URL |
| Base URL | `https://graphql.api.rye.com` / `staging.graphql.api.rye.com` | `https://api.rye.com/api/v1/` / `https://staging.api.rye.com/api/v1/` |
| Supported merchants | Amazon + Shopify | Any merchant |
| Primary resource | `Cart` → `Store[]` → `Order[]` | `CheckoutIntent` (state machine) |
| Status model | Fat webhooks + polling | Thin webhooks + polling |

### Use the SDK (applies to all phases)

We **strongly recommend adopting the official SDK** (TypeScript, Python, or Java) rather than hand-rolling HTTP requests. The SDK handles auth, pagination, retry, and schema typing so you stay insulated from future protocol-level changes.

Your existing API key is unchanged; simply hand it to the SDK client:

```ts theme={null}
import { CheckoutIntents } from "checkout-intents";
const rye = new CheckoutIntents({
  // RYE/production-xyz...
  apiKey: process.env.RYE_API_KEY,
});
```

```python theme={null}
from checkout_intents import CheckoutIntents
rye = CheckoutIntents(api_key=os.environ["RYE_API_KEY"])
```

***

## Phase 1 — Product catalog

**V1 model:** required explicit opt-in to track individual products via `requestAmazonProductByURL` / `requestShopifyProductByURL`, then product data could be queried via GraphQL.

**V2 model:** on-demand lookup + push-based updates for Shopify.

### How to get product data

* Use the [lookup product endpoint](/api-v2/api-reference/products/lookup-product) on whatever cadence makes sense for your use case (e.g. on-demand at render time, nightly refresh of a curated catalog, etc.). There is no pre-sync step; you can immediately look up product data for anything you have a URL for.
* For Shopify merchants onboarded via your [Shopify app installation link](https://docs-v1.rye.com/sync-api/shopify-merchant-onboarding/shopify-generate-installation-link), you continue to receive real-time catalog updates via webhooks, in the form of `product.updated` events.

### Product webhooks

* Product webhooks in V2 are snapshots, similar to webhooks in the GraphQL API: the payload contains the current product state. You do **not** need to call the API back to fetch updated data.
* To start receiving V2 product webhooks, **re-save your webhook URL in the developer console**. The same URL can be re-submitted — the re-save is what migrates the subscription onto V2 delivery.
* The V2 signature header is `x-rye-signature` (HMAC-SHA256). Also consume `x-rye-event-id` (for idempotency) and `x-rye-timestamp` (for replay windows).

### What to do

* Update your code to use the new Universal Checkout API `Product` type.
* Look up product data with the [lookup product endpoint](/api-v2/api-reference/products/lookup-product), instead of `productByID` or `productsByDomainV2` queries.
* Delete `requestAmazonProductByURL` / `requestShopifyProductByURL` pre-sync calls.
* Sub out any `PRODUCT_UPDATED` webhook handlers for `product.updated` handlers.

***

## Phase 2 — Ordering

The V1 `Cart` model collapses into V2's `CheckoutIntent`. One intent maps to one product URL and one buyer.

Most GraphQL integrations only ordered one product per cart, but if you require multi-item or multi-merchant checkout you can do this by creating multiple checkout intents and aggregating the results in your code.

### Flow mapping

| V1 GraphQL op | V2 REST call | Notes |
| - | - | - |
| `createCart` | `POST /api/v1/checkout-intents` | |
| *(wait for offer)* | `GET /api/v1/checkout-intents/{id}` until `state=awaiting_confirmation` | Poll or use the `checkout_intent.offer_retrieved` webhook. |
| `updateCartBuyerIdentity` | `POST /api/v1/checkout-intents` | Checkout intents are immutable; create a new one if you need to change shipping address |
| `updateCartSelectedShippingOptions` | *(gone)* | Rye selects shipping during offer retrieval. |
| `submitCart` | `POST /api/v1/checkout-intents/{id}/confirm` | Submit checkout intent for purchase. |
| `orderByID` / `checkoutByCartID` | `GET /api/v1/checkout-intents/{id}` | When intent reaches `completed` state, it carries merchant order IDs. |

State machine: `retrieving_offer → awaiting_confirmation → placing_order → completed | failed`.

### Payment providers

The Universal Checkout API supports [many different payment providers](/api-v2/payment-providers), but our general recommendations are:

* **B2C apps → `basis_theory_token`.** Cards are tokenized via Basis Theory and forwarded to the merchant vault where possible. Either Rye or the merchant is MoR.
* **B2B apps → `drawdown`.** Pre-fund a Rye balance and draw down per purchase. No per-transaction tokenization. You are MoR.

### Gotchas

* **Rate limits are split across multiple buckets.** See the [Rate Limits](/api-v2/rate-limits) guide for the full breakdown — request an increase before cutover if you move more volume than the defaults.
* **Error shape changed.** V1 returns `errors` inside the GraphQL payload; V2 returns HTTP status codes + a REST error body. Your retry/backoff code will need to be updated if you don't use an official SDK.
* **Variants:** V2 accepts either a variant-deep-link URL or `variantSelections`.

***

## Phase 3 — Post-purchase

### Shipments & tracking

V1 exposed shipments via `Order.shipments`. V2 has first-class endpoints:

* `GET /api/v1/shipments` — cursor-paginated (`after`, `before`, `ids`), filterable.
* `GET /api/v1/shipments/{id}`
* `GET /api/v1/checkout-intents/{id}/shipments`

Replace nested-object reads with REST paginated fetches.

### Order-state webhooks

V1 published rich event-specific payloads (`ORDER_PLACED`, `PAYMENT_SUCCEEDED/FAILED/REFUNDED`, `REFUND_CREATED`, …). V2 ships four thin events carrying only `{id, type, source:{type, id}}` — you re-fetch the intent on receipt.

| V2 event | Roughly replaces |
| - | - |
| `checkout_intent.offer_retrieved` | signal to move from `retrieving_offer` → confirm |
| `checkout_intent.offer_failed` | cart-level errors surfaced at create time |
| `checkout_intent.completed` | `ORDER_PLACED` + `PAYMENT_SUCCEEDED` |
| `checkout_intent.order_failed` | `PAYMENT_FAILED` / order failure paths |

We have a guide for setting up webhooks [here](/api-v2/webhooks).

### Returns

* V1 had return queries; V2 does not expose a returns API.
* **Initiate returns by emailing `orders@rye.com`** with the order/intent ID and return reason.
* Plan for out-of-band handling in your support tooling.

***

## Suggested cutover sequence

1. Install the V2 SDK (TypeScript / Python / Java) and wire it up with your existing API key. No key rotation needed.
2. **Phase 1:** re-save webhook URL in the console; migrate product-data reads to the lookup endpoint; drop pre-sync calls.
3. **Phase 2:** port one low-volume merchant flow to `POST /checkout-intents` + polling. Pick Basis Theory (B2C) or Drawdown (B2B) for payments.
4. **Phase 3:** swap `Order.shipments` reads for `/shipments` endpoints; update the webhook handler to re-fetch intents; route return requests to `orders@rye.com`.
5. Drain in-flight V1 carts at cutover — don't dual-write.
6. `requestAmazon/ShopifyProductByURL` and `updateCartSelectedShippingOptions` callsites.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.