# Build a checkout with the Store API

> Use a publishable key to list products, fill a cart, take payment and show orders on your own site.

Source: https://www.coritan.com/docs/organizations/storefront/store-api/

The Store API is what your storefront calls to sell the goods in your organization's store. It lists the catalogue, fills carts, takes payment, places orders and shows shoppers their orders. You set up and run the store with the [Commerce API](/docs/organizations/storefront/commerce-api/).

Every route is under `https://api.coritan.com/api/v1/orgs/{org_slug}/store`. The API reference lists them under [Commerce Store API](/docs/api/reference/organizations/commerce-store-api/), with the product routes under [Catalog & Services](/docs/api/reference/organizations/catalog-services/store/) and the shopper's account routes under [Customers](/docs/api/reference/organizations/customers/store/).

## Before you begin

- Set up the store with the Commerce API: a region, a stock location, shipping, and published products with prices. [Set up the store](/docs/organizations/storefront/commerce-api/#set-up-the-store) explains each part.
- Create a publishable key, as [Create publishable keys and sales channels](/docs/organizations/storefront/commerce-api/#create-publishable-keys-and-sales-channels) shows. Build with a test key, whose token starts with `pk_test_`. A live key works once we switch the store to `live`.
- Set the store's `storefront_url`, because the order emails link to pages on it, and list your storefront's address in `allowed_origins`.
- We send order emails with your organization's email settings. [Choose how your email leaves](/docs/organizations/settings/#choose-how-your-email-leaves) before you take orders.
- To let shoppers sign in, add [customer sign-in](/docs/organizations/storefront/customer-sign-in/) to your storefront. Shoppers can also check out as guests, unless you turn off the store's `guest_checkout` setting.

## Send the publishable key

Send the key in the `x-publishable-api-key` header of every request. It names your store and cannot change it, so it is safe to put in the browser.

```bash
curl "https://api.coritan.com/api/v1/orgs/acme/store" \
  -H "x-publishable-api-key: $PUBLISHABLE_KEY"
```

The answer is the `store` as your storefront needs it: its `name`, whether the key is in `livemode`, the `currencies` and `locales` with their defaults, the `default_region_id`, the key's `sales_channels`, the `storefront_url` and `support_email`, the `terms_url` and `privacy_url`, and whether `guest_checkout` is on. The `store` also holds `turnstile`, with `enabled` and the widget's `site_key`: when `enabled` is `true`, show the Cloudflare Turnstile challenge with that key on your sign-up form and send its answer as `turnstile_token`, as [Register a customer](/docs/organizations/storefront/customer-sign-in/#register-a-customer) shows.

- A test key reaches test carts and orders only, and a live key reaches live ones. A cart or order of the other mode answers `404`.
- A key sells the products of its sales channels. The catalogue routes and new carts take a `sales_channel_id` to narrow to one of them.
- When the shopper is signed in, also send their customer token as `Authorization: Bearer $CUSTOMER_TOKEN`. It prices products for the shopper's customer groups, makes the carts they change theirs, and opens their account and order routes.
- While the store lists no `allowed_origins`, a browser on any site can call the Store API. Once it lists some, only those sites can. A change to the list takes up to 30 seconds to apply.

## Show the catalogue

`GET /store/products` lists the published products in the key's sales channels, newest first.

| Parameter | Takes |
| --- | --- |
| `q` | Text to find in the title, subtitle, handle or tags |
| `collection_id` or `collection_handle` | A published collection. A smart collection follows its rules. |
| `category_id` or `category_handle` | A category, with every category under it |
| `tag` | One tag, in any case |
| `id` | A product ID, repeated for more, up to 100 |
| `order` | `-created_at` (the default), `created_at`, `title` or `-title` |
| `limit`, `offset` | Up to 100 a page; 20 by default |

```bash
curl "https://api.coritan.com/api/v1/orgs/acme/store/products?collection_handle=clothing&country_code=DE" \
  -H "x-publishable-api-key: $PUBLISHABLE_KEY"
```

Every answer is priced for one place: the `region_id` you send, else the region that sells to `country_code`, else `currency_code`, else the store's default region. The answer's `region` and `currency_code` say which it used. Each variant has:

- `calculated_price`, with the `amount` the shopper pays, the `original_amount` before any price list, the merchant's `compare_at_amount`, and `is_discounted`. It is `null` when the variant has no price there.
- `purchasable`, which is `true` when the variant has a price and can be ordered.
- `availability`: `in_stock`, `low_stock` (5 or fewer left), `backorder`, `out_of_stock`, or `unlimited` for a variant that does not track stock. Stock counts never leave our servers.

`GET /store/products/{id_or_handle}` answers one product by its handle or ID, priced the same way.

For menus and pickers:

- `GET /store/collections` lists the published collections, and `GET /store/collections/{handle}` answers one. Its products come from `GET /store/products?collection_handle=...`.
- `GET /store/categories` answers the categories as a tree, and `GET /store/categories/{handle}` answers one with its `children` and `parent`. A category that is inactive or internal is hidden, with everything under it.
- `GET /store/regions` lists the regions with their `countries`, `currency_code`, `tax_inclusive` and `payment_providers`, for a country picker. `GET /store/regions/{region_id}` answers one.

Catalogue answers to requests without a shopper's token can be cached for up to 30 seconds, so a change to a product can take that long to show.

## Fill a cart

Create the cart when the shopper adds their first item. Send their country, and the cart takes the region that sells there, with its currency:

```bash
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/store/carts" \
  -H "x-publishable-api-key: $PUBLISHABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"country_code": "DE", "email": "alex@example.com", "items": [{"variant_id": 1841, "quantity": 2}]}'
```

The answer is `201` with the whole `cart`. Keep its `id`, which starts with `cart_`, in the shopper's browser. Every change answers the whole cart again, priced from the catalogue as it is at that moment.

- Without `country_code` or `region_id`, the cart takes the store's default region. A country the store does not sell to answers `422` with `country_not_served`.
- `POST /store/carts/{cart_id}/line-items` adds a `variant_id` with a `quantity`. The same variant with the same `metadata` joins its existing line. A line holds up to 999 units, and a cart up to 100 lines.
- `PATCH /store/carts/{cart_id}/line-items/{line_id}` sets a line's `quantity`, and `0` removes the line. `DELETE` on the same path removes it too.
- `PATCH /store/carts/{cart_id}` sets the `email`, the `shipping_address` and `billing_address`, a `note` of up to 2,000 characters, the `locale` and `metadata`. A new `country_code` or `region_id` moves the cart to that region and prices every line in its currency.
- Adding an item holds no stock: we reserve it when the order is placed. Asking for more than there is answers `409` with `insufficient_inventory`, and `available` gives the units left for each variant.

Anyone who holds a guest cart's ID can reach it. When a signed-in shopper changes a cart, it becomes theirs, and anyone else then gets `403` with `cart_forbidden`. Only an `open` cart changes, and a cart that nobody touches for 30 days becomes `abandoned`.

An address takes `first_name`, `last_name`, `company`, `address_1`, `address_2`, `city`, `province`, `postal_code`, `country_code` and `phone`. To ship and tax an order, it needs the `country_code`, a first or last name, `address_1`, `city`, and a `postal_code` where the country uses them. A Canadian address also needs its `province`, as a code such as `ON` or a name such as `Ontario`, because the tax depends on it. The shipping country must be one of the cart's region.

To sell a gift card, put its recipient in the line's `metadata`: `recipient_email`, `recipient_name`, `sender_name` and a `message` of up to 500 characters. We issue one card for each unit, and email it to the recipient once the order is placed, or to the shopper when the line names nobody.

### Apply codes and gift cards

- `POST /store/carts/{cart_id}/promotions` with a `code` applies a discount code. A code that does not apply answers `422` with `promotion_not_applicable`, a `reason` such as `expired`, `min_subtotal` or `first_order_only`, and a `message` you can show. A cart holds up to 5 codes. Remove one with `DELETE /store/carts/{cart_id}/promotions?code=WELCOME10`. Automatic promotions apply without a code.
- `POST /store/carts/{cart_id}/gift-cards` with a `code` applies a gift card, up to 5 a cart. A wrong code answers `422` with `gift_card_invalid`, and a card that cannot pay answers `422` with `gift_card_not_applicable` and a `reason`: `disabled`, `expired`, `exhausted` or `currency_mismatch`. The cart shows each card's `last4` and the amount it `applied`.

Each cart, and each IP address, gets 10 gift card attempts in 10 minutes, right or wrong. When the cart changes, codes and cards that no longer apply leave it.

## Choose shipping

Once the cart has a shipping address, `GET /store/carts/{cart_id}/shipping-options` lists the options that ship its items there, priced for them. Each option has its `id`, `name`, `profile_id`, `amount` and `currency_code`, whether it `is_pickup`, and its `delivery_min_days` and `delivery_max_days`.

Items in different shipping profiles need one option for each profile. Choose one with `POST /store/carts/{cart_id}/shipping-methods` and its `option_id`: it replaces the method that profile had. A cart with nothing to ship, such as one that holds only gift cards, needs a billing address instead of shipping.

## Check the cart before payment

The cart's `totals` hold what the shopper pays: the `subtotal`, `discount_total`, `shipping_total`, `tax_total` and `gift_card_total`, and the `total` that is left to pay after gift cards. Each line has its own `totals`. Until the cart has an address, `tax_estimated` is `true` and the tax is an estimate for the region's first country.

We work out the tax from Coritan's own rates. Each tax in a line's `tax_lines`, or a shipping method's, has its `code`, `name`, `rate` (a fraction, such as `0.19`) and `amount`, and the `jurisdiction` it is owed to: the `country_code`, and the `subdivision_code` of a state or province that charges it.

The cart's `blockers` list what stops checkout, and `completable` is `true` when the list is empty.

| Blocker | What to do |
| --- | --- |
| `empty` | Add an item. |
| `region_unavailable` | The cart's region was disabled. Move the cart to another region. |
| `item_unavailable` | An item is no longer for sale. Remove it. |
| `insufficient_inventory` | An item has less stock than the cart asks for. Lower the quantity. |
| `price_unavailable` | An item has no price in the cart's currency. Remove it. |
| `email_required` | Set the cart's `email`. |
| `customer_required` | The store does not take guest orders. Sign the shopper in. |
| `shipping_address_required` | Add a complete shipping address. |
| `billing_address_required` | Nothing in the cart ships, so add a complete billing address. |
| `shipping_method_required` | Choose a shipping option for each profile. |
| `tax_unavailable` | We cannot work out the tax for the address. A Canadian address needs its `province`. For an address elsewhere, ask [support](https://www.coritan.com/dashboard/support). |

Starting a payment or completing the cart while something blocks it answers `409` with `cart_not_ready` and the `blockers`. There, the list can also hold `seller_unavailable`, which means no Coritan company sells to the address.

## Take payment

1. List what can take the payment with `GET /store/carts/{cart_id}/payment-providers`. Each provider has an `id`, a `display_name` and the `config` its browser script needs. The answer also has the `total` and `payment_required`, which is `false` when gift cards pay for everything: then go straight to [Complete the order](#complete-the-order).
2. Start a payment session with the provider the shopper picks. Send `accept_terms: true` once the shopper has accepted your terms.

   ```bash
   curl -X POST "https://api.coritan.com/api/v1/orgs/acme/store/carts/cart_01j8z4a7b2c9d3e6f5g8h1k0m4/payment-sessions" \
     -H "x-publishable-api-key: $PUBLISHABLE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"provider": "stripe", "accept_terms": true}'
   ```

3. Let the shopper pay in the browser with the `payment_session` from the answer:
   - `stripe`: mount Stripe's Payment Element with `config.publishable_key` and the session's `client_secret`, and confirm the payment with Stripe.js.
   - `paypal`: send the shopper to the session's `approve_url`, or show PayPal's buttons with `config.client_id` and the `paypal_order_id`. PayPal brings the shopper back to the `return_url` you sent, or to the `cancel_url` when they stop. Both must be on your storefront's origin or in `allowed_origins`, and both default to `<storefront_url>/checkout`.
   - `manual`: a test payment that moves no money. Only test carts offer it, and it is authorized at once.

Starting a session again with the same provider for the same amount answers the same session, and a session with another provider replaces it. When the cart changes after the payment started, start the session again. The shopper pays the Coritan company that sells to their address, which also issues the invoice.

## Complete the order

`POST /store/carts/{cart_id}/complete` checks the payment with the provider and places the order. Send an `Idempotency-Key` header: a value of up to 128 characters that you make once for each order the shopper places.

```bash
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/store/carts/cart_01j8z4a7b2c9d3e6f5g8h1k0m4/complete" \
  -H "x-publishable-api-key: $PUBLISHABLE_KEY" \
  -H "Idempotency-Key: 5f0c2a6e-1d3b-4c7e-9a41-2b8f6d0e7c19" \
  -H "Content-Type: application/json" \
  -d '{"accept_terms": true}'
```

The answer is `200` with the `order` and the `access_token` for its page. We email the shopper a confirmation that links to `<storefront_url>/orders/{order_id}?token=<access_token>`, a page your storefront serves.

- A retry with the same key for the same cart, within 24 hours, answers what the first request answered. A cart that already has its order answers that order again, with or without the key.
- The same key for another cart answers `409` with `idempotency_key_reused`, and a retry while the first request still runs answers `409` with `idempotency_in_progress`.
- We price the cart again at this step. When its total no longer matches the payment, the answer is `409` with `payment_session_stale`: start the payment again.
- When the money moved but the order cannot be placed, for example because another shopper bought the last unit, we refund the payment. The answer is `409` with the reason and `payment_refunded: true`.
- When the shopper closes the page after paying, we place the order once the payment provider reports the payment.
- A guest's first order adds a customer with that email to your organization, without a password. Their later orders join the same customer.

| Answer | What to do |
| --- | --- |
| `409` with `payment_required` | Start a payment session first. |
| `409` with `payment_requires_action` | The shopper has not finished paying, for example approving in PayPal. Let them finish, then complete the cart again. |
| `409` with `payment_failed` | The payment was declined. Let the shopper pay again. |
| `409` with `payment_processing` | The payment is still clearing. We place the order as soon as it clears. |
| `409` with `cart_completing` | Another request is placing this order. Complete the cart again in a moment to get the order. |
| `503` with `completion_pending` | The payment went through and we are placing the order. Try again in a minute. |
| `503` with `payment_provider_unavailable` | The payment provider did not answer. Try again in a moment. |
| `401` with `sign_in_required` | The store takes no guest orders. Sign the shopper in. |
| `422` with `terms_not_accepted` | Show the terms at `terms_url`, then send `accept_terms: true`. |

A cart gets 20 payment sessions and 20 completion attempts in 10 minutes, and an IP address gets 60 of each. Beyond that, the answer is `429` with `rate_limited` and a `Retry-After` header.

## Show the shopper's orders

`GET /store/orders/{order_id}` answers an order to its signed-in shopper, or to anyone who sends its `token`: the `access_token` from completion, which every order email carries. A token works for 90 days after the order was placed, or for the store's `order_access_days`.

```bash
curl "https://api.coritan.com/api/v1/orgs/acme/store/orders/order_01j8z3k4m5n6p7q8r9s0t1v2w3?token=$ORDER_TOKEN" \
  -H "x-publishable-api-key: $PUBLISHABLE_KEY"
```

The order has its `items`, `totals`, `shipping_methods` and `refunds`, the `fulfillments` with their tracking, its `documents`, a `timeline` of what the shopper may see, such as shipments and the notes you share, and `sold_by`, the Coritan company that sold it.

- `GET /store/orders/{order_id}/documents` answers the invoice and any credit notes as data, with the issuer, the buyer, the lines and the tax, for your storefront to show.
- `GET /store/customers/me/orders` lists a signed-in shopper's orders in the key's mode, newest first: 20 a page by default, and up to 100.
- `POST /store/orders/lookup` with an `email`, and optionally an `order_number` such as `#1001`, emails the links to up to 10 orders placed with that address while their tokens still work. The answer is always `202` with `sent: true`, so it never shows whether an address has ordered. An IP address can look up 10 times an hour, and each email address 3 times.

## Let the shopper ask for a return

A shopper can ask to send back units that have shipped, or to exchange them for other variants. The request waits as `requested` for you to approve or decline it with the Commerce API, as [Handle returns, exchanges and claims](/docs/organizations/storefront/commerce-returns/) explains.

`GET /store/return-reasons` lists the reasons the shopper can pick, ordered by the `rank` you give them, each with its `code`, `label` and `description`. It leaves out the reasons you disabled.

The return routes of an order answer its signed-in shopper, or anyone who sends its `token`, as `GET /store/orders/{order_id}` does. Send the request with the items that go back:

```bash
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/store/orders/order_01j8z3k4m5n6p7q8r9s0t1v2w3/returns?token=$ORDER_TOKEN" \
  -H "x-publishable-api-key: $PUBLISHABLE_KEY" \
  -H "Idempotency-Key: 2c7e9b14-8f3a-4d6b-a1e5-7b0d3c9f6a28" \
  -H "Content-Type: application/json" \
  -d '{"items": [{"order_item_id": 5521, "quantity": 1, "reason_code": "too_small"}], "note": "The shoes are half a size too small."}'
```

| Field | Takes |
| --- | --- |
| `items` | Up to 100 of the order's items, each with its `id` as `order_item_id` and the `quantity` that goes back. Each can add a `reason_code` from the store's reasons and a `note` of up to 500 characters. |
| `exchange_items` | For an exchange, up to 50 variants the shopper wants instead, each a `variant_id` and a `quantity`. |
| `note` | A note of up to 2,000 characters. |

The answer is `201` with the `return`, which is `requested`. Its `kind` is `exchange` when it names new items, and `return` otherwise. `refund_amount` is what we expect to refund. It can fall when you approve the return with a fee, or when fewer units come back.

- The shopper can ask for units that have shipped and are not already returned, refunded or in another return. Gift cards never come back.
- The request must come within the store's returns window. The window is 30 days unless you [set the store's `returns_window_days`](/docs/organizations/storefront/commerce-returns/#set-the-returns-window-and-the-reasons), and `0` turns shopper requests off. It counts from when the item was delivered, or shipped when no delivery is recorded. When the item arrived in more than one shipment, the latest one counts.
- We price the new items in the order's currency and region, for the shopper's customer groups, and tax them at the order's address. They must be for sale in the order's sales channel and in stock. They can cost no more than the units that go back are worth, because the Store API takes no payment for a difference.
- Send an `Idempotency-Key` header, as when you complete a cart. A retry with the same key for the same order, within 24 hours, answers what the first request answered.
- `GET /store/orders/{order_id}/returns` lists every return on the order, oldest first, including the ones you open yourself. Show each one's `status`, the `instructions` and `label_url` you add when you open or approve it, and the `rejection_reason` when you decline it.
- Once we create the order that sends an exchange's new items, the return's `exchange_order_id` names it, and `exchange_order_access_token` is its `token` for `GET /store/orders/{order_id}`.
- We email the shopper when a return is refunded, as for any refund. The other steps of a return send no email, so show them on your order page. The order's `timeline` shows them to the shopper too.

| Answer | What to do |
| --- | --- |
| `409` with `item_not_returnable` | Ask for no more than `returnable_quantity` units of the item in `order_item_id`. |
| `409` with `return_window_closed` | The window closed at `closed_at`. You can still open the return yourself with the Commerce API. |
| `409` with `exchange_payment_required` | The new items cost `difference_amount` more than what goes back. Let the shopper pick items that cost less, or ask for a refund. You can also open the exchange yourself and waive the difference. |
| `409` with `insufficient_inventory` | A new item has less stock than the shopper asks for, and `available` gives the units left. |
| `409` with `order_canceled` or `exchange_order` | The order was cancelled, or it sends the new items of an exchange. Neither takes a return, so show the shopper the store's `support_email`. |
| `422` with `invalid` on `items` or `exchange_items` | An `order_item_id` is not one of the order's items, a `reason_code` is not one of the store's reasons, or a new item is a gift card or is not for sale in the order's sales channel. |
| `429` with `rate_limited` | The order had 10 return requests in the last hour. `Retry-After` says when to try again. |

## Manage the shopper's account

These routes need the shopper's token, and reach only their own records:

- `GET /store/customers/me` answers the shopper's `email`, name, `phone`, `accepts_marketing` and `vat_id`. `PATCH` changes `first_name`, `last_name`, `phone`, `accepts_marketing` and `vat_id`. Only the shopper can turn marketing on.
- `GET /store/customers/me/addresses` lists their saved addresses, up to 50. `POST` adds one, which needs `address_1`, `city` and `country_code`. The first address becomes the default for shipping and billing, and `is_default_shipping` and `is_default_billing` move the defaults. `PATCH` and `DELETE` on `/store/customers/me/addresses/{address_id}` change and delete one.

## Errors

An error answers with a status and a `detail` object that holds a machine-readable `error` code and a `message` you can show the shopper. Some errors add fields, such as `blockers` or `available`.

```json
{"detail": {"error": "cart_not_ready", "message": "The cart cannot be completed yet.", "blockers": ["shipping_method_required"]}}
```

| Status | `error` | Meaning |
| --- | --- | --- |
| `401` | `publishable_key_required`, `invalid_publishable_key` | The key is missing, revoked or for another store. |
| `403` | `store_not_live` | A live key, while the store is still in test mode. |
| `403` | `cart_forbidden` | The cart belongs to another shopper. |
| `404` | `commerce_not_enabled` | The organization has no store, or Coritan disabled it. |
| `404` | `not_found` | The thing does not exist in this store or this mode, or the order is not the shopper's. |
| `409` | `cart_not_open` | The cart already has its order, or was abandoned. |
| `422` | `invalid` or a specific code | The input is wrong; `field` names it when it can. |
| `429` | `rate_limited` | Too many attempts. `Retry-After` says when to try again. |
| `503` | `store_unavailable` | Coritan suspended the store, so it takes no orders. |

## Result

With a test key, your storefront lists the catalogue, fills a cart, takes a test payment and places a test order. The order appears in the Commerce API's `GET /commerce/orders?livemode=false`, and the shopper receives a confirmation whose subject starts with `[TEST]`. Once the store is live, the same code with a live key takes real orders.

## Troubleshooting

`401` with `invalid_publishable_key`
: The key was revoked or mistyped, or belongs to another organization. List the keys with `GET /commerce/publishable-keys`, or create one.

`403` with `store_not_live`
: The store is still in `test` mode. Use a test key until we switch the store to `live`.

The browser reports a CORS error
: The store lists `allowed_origins`, and your site is not among them. Add its origin with `PATCH /commerce/store`. The change applies within 30 seconds.

`422` with `country_not_served` or `country_not_in_region`
: The store has no region for that country, or the address is outside the cart's region. Move the cart with `PATCH /store/carts/{cart_id}` and a `country_code`, or add the country to a region.

`409` with `price_unavailable`
: The variant has no price in the cart's currency. Give it one for that currency or region with the Commerce API.

`409` with `cart_not_ready`
: Something in `blockers` stops checkout. [Check the cart before payment](#check-the-cart-before-payment) says what each one needs.

`409` with `payment_already_authorized`
: The cart is already paid for. Complete it.

`409` with `cart_not_open`
: The cart is completed or abandoned. Completing a completed cart again answers its order; otherwise create a new cart.

`422` with `invalid` on `return_url` or `cancel_url`
: PayPal's return addresses must be on your storefront. Set the store's `storefront_url`, or add your site's origin to `allowed_origins`.

An order link answers `404`
: The token is wrong, or the order is older than the store's `order_access_days`. A signed-in shopper can still read their own orders.

## Related

- [Sell with the Commerce API](/docs/organizations/storefront/commerce-api/)
- [Handle returns, exchanges and claims](/docs/organizations/storefront/commerce-returns/)
- [Sign customers in to your storefront](/docs/organizations/storefront/customer-sign-in/)
- [Build a storefront on the Organization API](/docs/organizations/storefront/)

## API

- `GET /api/v1/orgs/{org_slug}/store`: Store info (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store)
- `GET /api/v1/orgs/{org_slug}/store/return-reasons`: List return reasons (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-return-reasons)
- `POST /api/v1/orgs/{org_slug}/store/carts`: A cart in the key's mode and sales channel, in a region and its currency (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-post-api-v1-orgs-org-slug-store-carts)
- `GET /api/v1/orgs/{org_slug}/store/carts/{cart_id}`: Get cart (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-carts-cart-id)
- `PATCH /api/v1/orgs/{org_slug}/store/carts/{cart_id}`: Email, addresses, region or country (re-prices every line), note, locale, metadata (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-patch-api-v1-orgs-org-slug-store-carts-cart-id)
- `POST /api/v1/orgs/{org_slug}/store/carts/{cart_id}/complete`: Place the cart's order: {"order", "accesstoken"} (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-post-api-v1-orgs-org-slug-store-carts-cart-id-complete)
- `POST /api/v1/orgs/{org_slug}/store/carts/{cart_id}/gift-cards`: Apply a gift card by its code: 10 attempts per 10 minutes per cart and per client IP (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-post-api-v1-orgs-org-slug-store-carts-cart-id-gift-cards)
- `DELETE /api/v1/orgs/{org_slug}/store/carts/{cart_id}/gift-cards/{gift_card_id}`: Remove gift card (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-delete-api-v1-orgs-org-slug-store-carts-cart-id-gift-cards-gift-card-id)
- `POST /api/v1/orgs/{org_slug}/store/carts/{cart_id}/line-items`: Add a variant; the same variant with the same metadata merges into its line (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-post-api-v1-orgs-org-slug-store-carts-cart-id-line-items)
- `PATCH /api/v1/orgs/{org_slug}/store/carts/{cart_id}/line-items/{line_id}`: Set a line's quantity; 0 removes it (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-patch-api-v1-orgs-org-slug-store-carts-cart-id-line-items-line-id)
- `DELETE /api/v1/orgs/{org_slug}/store/carts/{cart_id}/line-items/{line_id}`: Delete line item (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-delete-api-v1-orgs-org-slug-store-carts-cart-id-line-items-line-id)
- `GET /api/v1/orgs/{org_slug}/store/carts/{cart_id}/payment-providers`: The providers that can take this cart's payment, and whether it needs one (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-carts-cart-id-payment-providers)
- `POST /api/v1/orgs/{org_slug}/store/carts/{cart_id}/payment-sessions`: Start the payment with provider, or refresh the one already started for the same amount (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-post-api-v1-orgs-org-slug-store-carts-cart-id-payment-sessions)
- `POST /api/v1/orgs/{org_slug}/store/carts/{cart_id}/promotions`: Apply a code (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-post-api-v1-orgs-org-slug-store-carts-cart-id-promotions)
- `DELETE /api/v1/orgs/{org_slug}/store/carts/{cart_id}/promotions`: Remove a code, named in the body {"code"} or as ?code= (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-delete-api-v1-orgs-org-slug-store-carts-cart-id-promotions)
- `POST /api/v1/orgs/{org_slug}/store/carts/{cart_id}/shipping-methods`: Choose an option; it replaces the method its shipping profile had (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-post-api-v1-orgs-org-slug-store-carts-cart-id-shipping-methods)
- `GET /api/v1/orgs/{org_slug}/store/carts/{cart_id}/shipping-options`: The options the cart's address and items allow, priced for them, per shipping profile (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-carts-cart-id-shipping-options)
- `GET /api/v1/orgs/{org_slug}/store/categories`: The active, public categories as a tree (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-categories)
- `GET /api/v1/orgs/{org_slug}/store/categories/{handle}`: Get category (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-categories-handle)
- `GET /api/v1/orgs/{org_slug}/store/collections`: List collections (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-collections)
- `GET /api/v1/orgs/{org_slug}/store/collections/{handle}`: A published collection; its products are /store/products?collectionhandle= (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-collections-handle)
- `GET /api/v1/orgs/{org_slug}/store/customers/me`: Get me (https://www.coritan.com/docs/api/reference/organizations/customers/store/#op-get-api-v1-orgs-org-slug-store-customers-me)
- `PATCH /api/v1/orgs/{org_slug}/store/customers/me`: Name, phone, marketing consent (the shopper alone turns it on) and VAT id (https://www.coritan.com/docs/api/reference/organizations/customers/store/#op-patch-api-v1-orgs-org-slug-store-customers-me)
- `GET /api/v1/orgs/{org_slug}/store/customers/me/addresses`: List my addresses (https://www.coritan.com/docs/api/reference/organizations/customers/store/#op-get-api-v1-orgs-org-slug-store-customers-me-addresses)
- `POST /api/v1/orgs/{org_slug}/store/customers/me/addresses`: Add my address (https://www.coritan.com/docs/api/reference/organizations/customers/store/#op-post-api-v1-orgs-org-slug-store-customers-me-addresses)
- `PATCH /api/v1/orgs/{org_slug}/store/customers/me/addresses/{address_id}`: Update my address (https://www.coritan.com/docs/api/reference/organizations/customers/store/#op-patch-api-v1-orgs-org-slug-store-customers-me-addresses-address-id)
- `DELETE /api/v1/orgs/{org_slug}/store/customers/me/addresses/{address_id}`: Delete my address (https://www.coritan.com/docs/api/reference/organizations/customers/store/#op-delete-api-v1-orgs-org-slug-store-customers-me-addresses-address-id)
- `GET /api/v1/orgs/{org_slug}/store/customers/me/orders`: The shopper's orders in the key's mode, newest first (https://www.coritan.com/docs/api/reference/organizations/customers/store/#op-get-api-v1-orgs-org-slug-store-customers-me-orders)
- `POST /api/v1/orgs/{org_slug}/store/orders/lookup`: Look up orders (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-post-api-v1-orgs-org-slug-store-orders-lookup)
- `GET /api/v1/orgs/{org_slug}/store/orders/{order_id}`: Get order (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-orders-order-id)
- `GET /api/v1/orgs/{org_slug}/store/orders/{order_id}/documents`: The invoice and credit notes, as issued by the seller of record (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-orders-order-id-documents)
- `GET /api/v1/orgs/{org_slug}/store/orders/{order_id}/returns`: The order's returns, oldest first (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-orders-order-id-returns)
- `POST /api/v1/orgs/{org_slug}/store/orders/{order_id}/returns`: Request return (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-post-api-v1-orgs-org-slug-store-orders-order-id-returns)
- `GET /api/v1/orgs/{org_slug}/store/products`: Published products in the key's sales channels (https://www.coritan.com/docs/api/reference/organizations/catalog-services/store/#op-get-api-v1-orgs-org-slug-store-products)
- `GET /api/v1/orgs/{org_slug}/store/products/{id_or_handle}`: One published product by handle (or by id), priced like the list (https://www.coritan.com/docs/api/reference/organizations/catalog-services/store/#op-get-api-v1-orgs-org-slug-store-products-id-or-handle)
- `GET /api/v1/orgs/{org_slug}/store/regions`: The regions the store sells in, to pick a country and currency from (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-regions)
- `GET /api/v1/orgs/{org_slug}/store/regions/{region_id}`: Get region (https://www.coritan.com/docs/api/reference/organizations/commerce-store-api/#op-get-api-v1-orgs-org-slug-store-regions-region-id)
