Guide

Cart sessions

Create and own a Throttle cart directly from the browser — no backend required. Cart sessions are the front door for JAMstack / static storefronts that don't run a server with a secret key.

Typed SDK + React hook
Prefer a typed client? CartSessionClient in @usethrottle/cart wraps every endpoint below — create/resume a session, then addItem, selectShipping, applyDiscount, checkout. In React, the useCartSession hook in @usethrottle/checkout-react adds reactive cart state + automatic localStorage persistence. The raw HTTP API is documented here for reference.

How it works

Server-side integrations build carts with a secret sk_ key. A frontend-only storefront can't hold a secret key, so it uses a cart session instead:

  • You create a cart session with your publishable quote token (pk_… — the same token used for shipping/tax quotes) from an allowed origin.
  • Throttle creates the cart and returns an opaque cart_… session id. That id is a bearer capability scoped to exactly one cart — hold it client-side (e.g. in localStorage) and use it to add, update, and remove items.
  • Because the cart lives in Throttle from the first add-to-cart, you get abandoned-cart recovery and a clean hand-off to checkout for free.
Two gates, no API key
Creation is authorized by your publishable token + the application origin allowlist. Every later call is authorized by possession of the opaque cart_… id + origin. No secret key is ever exposed to the browser, and a session can only ever touch its own cart.

Set your allowed origins first

Cart-session requests are rejected unless the request Origin is on the application's allowlist (the same allowlist used by embedded checkout and quotes). Set it via PUT /api/v1/embed-config.

Create a cart session

bash
curl -X POST https://api.usethrottle.dev/api/v1/cart-sessions \
  -H "Origin: https://shop.example.com" \
  -H "Content-Type: application/json" \
  -d '{
    "applicationId": "7f9d4c8a-5b2e-4f16-9a73-2d1e5c8b6f40",
    "environmentId":  "a1b2c3d4-...",
    "quoteToken": "pk_live_...",
    "currency": "USD"
  }'

Returns cartSessionId (the cart_… token), expiresAt, and a cart snapshot.

Build the cart

bash
# Add an item
curl -X POST https://api.usethrottle.dev/api/v1/cart-sessions/cart_<id>/items \
  -H "Origin: https://shop.example.com" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Premium Widget", "unitPrice": 2999, "quantity": 2 }'

# Update an item
curl -X PATCH https://api.usethrottle.dev/api/v1/cart-sessions/cart_<id>/items/<itemId> \
  -H "Origin: https://shop.example.com" -H "Content-Type: application/json" \
  -d '{ "quantity": 3 }'

# Remove an item
curl -X DELETE https://api.usethrottle.dev/api/v1/cart-sessions/cart_<id>/items/<itemId> \
  -H "Origin: https://shop.example.com"

# Read the current cart
curl https://api.usethrottle.dev/api/v1/cart-sessions/cart_<id> \
  -H "Origin: https://shop.example.com"

Shipping & discounts

Fetch live rates from POST /api/v1/shipping-tax/quotes (same publishable token), then select a method on the session. Apply a promo code the same way.

bash
# Select a shipping method
curl -X POST https://api.usethrottle.dev/api/v1/cart-sessions/cart_<id>/shipping \
  -H "Origin: https://shop.example.com" -H "Content-Type: application/json" \
  -d '{ "methodId": "fedex_ground", "displayName": "FedEx Ground", "rateAmount": 599 }'

# Apply a discount code
curl -X POST https://api.usethrottle.dev/api/v1/cart-sessions/cart_<id>/discount \
  -H "Origin: https://shop.example.com" -H "Content-Type: application/json" \
  -d '{ "code": "SAVE10" }'

Hand off to checkout

When the buyer is ready, convert the cart session into a checkout session and redirect them to the returned checkoutUrl. This marks the cart session converted (no further edits).

bash
curl -X POST https://api.usethrottle.dev/api/v1/cart-sessions/cart_<id>/checkout-session \
  -H "Origin: https://shop.example.com" -H "Content-Type: application/json" \
  -d '{
    "returnUrl": "https://shop.example.com/thanks",
    "cancelUrl": "https://shop.example.com/cart"
  }'

One hook for the whole flow: useThrottleCheckout

useThrottleCheckout (in @usethrottle/checkout-react) wraps all of the above: it binds totals and selectedMethod straight to the cart (no shadow copy to drift), locks shipping in one call via selectMethod, exposes a status state machine, and auto-recovers from a stale cart — if the cart was converted by a completed order or abandoned, it rebuilds a fresh session from the last-known line items and retries (status === 'recovering'). createSession returns the checkoutSessionId for <PaymentEmbed>.

tsx
import { useThrottleCheckout, PaymentEmbed } from '@usethrottle/checkout-react';

function Checkout() {
  const {
    addItem, selectMethod,   // one-call select — returns the recomputed cart
    totals, selectedMethod,  // bound to the cart; never a stale copy
    status,                  // 'idle' | 'loading' | 'recovering' | 'ready' | 'error'
    createSession,           // → { checkoutSessionId } for <PaymentEmbed>
  } = useThrottleCheckout({ applicationId, environmentId, quoteToken });

  const start = async () => {
    const { checkoutSessionId } = await createSession({
      returnUrl: 'https://shop.example.com/thanks',
      cancelUrl: 'https://shop.example.com/cart',
    });
    setSessionId(checkoutSessionId);
  };

  return sessionId
    ? <PaymentEmbed sessionId={sessionId} parentOrigin={location.origin} />
    : <button onClick={start} disabled={status === 'loading'}>Pay {totals?.total}</button>;
}
Address collection lives in the embed
The hook does not write addresses to the cart — the hosted/full checkout embed collects shipping & billing. The hook focuses on cart state, shipping selection, totals, recovery, and the session handoff.

Endpoints

  • POST /api/v1/cart-sessions — create a cart + session.
  • GET /api/v1/cart-sessions/{id} — read the session + cart.
  • POST /api/v1/cart-sessions/{id}/items — add an item.
  • PATCH /api/v1/cart-sessions/{id}/items/{itemId} — update an item.
  • DELETE /api/v1/cart-sessions/{id}/items/{itemId} — remove an item.
  • POST /api/v1/cart-sessions/{id}/shipping — select a shipping method.
  • DELETE /api/v1/cart-sessions/{id}/shipping — clear the selected method.
  • POST /api/v1/cart-sessions/{id}/discount — apply a discount code.
  • DELETE /api/v1/cart-sessions/{id}/discount — remove the discount.
  • POST /api/v1/cart-sessions/{id}/checkout-session — hand off to checkout.
Errors
invalid_quote_token (401) — bad token, app/environment, or origin. origin_not_allowed (403) — origin not on the allowlist. cart_session_expired (410) — session is no longer active. item_not_found (404) — the item isn't in this session's cart. rate_limit_exceeded (429) — too many POST /cart-sessions creates from one client; back off and retry.