# Claiming a cart

A visitor typically builds a cart anonymously and only logs in later. Claiming binds that anonymous cart to the logged-in customer identified by `X-Customer-Token`. From then on, the cart is customer-owned: requests against it must carry the owner's token (see the ownership rules on the Carts page), prices resolve in the customer's pricing context, and checkout binds the order to the customer.

## Merge behavior

If the customer already had another `active` cart, its lines are folded into the claimed cart:

- Lines dedupe on the combination of product, variant, and configuration: matching lines have their quantities summed, then clamped to the policy's `max_quantity_per_line`.
- Extra distinct lines from the old cart are dropped once the claimed cart reaches `max_lines`.
- The old cart's status becomes `merged` and its stock holds are released. A merged cart id keeps working on `GET /carts/{id}` but returns 404 on writes.

Claiming a cart that already belongs to this same customer is a no-op and returns the cart unchanged. Claiming a cart that belongs to a different customer fails with 403.

Note that a cart bound to a guest customer row via email capture (`PATCH /carts/{id}` with `email`) has `customer_id` set too: claiming it succeeds only when the token resolves to that same customer; otherwise it is the 403 case.

## POST /v1/api/commerce/carts/{id}/claim

Requires `commerce:write` and an `active` cart. `X-Customer-Token` is mandatory: without a resolvable token the request fails with status 401 and an empty body. No request body. Returns the full cart envelope, the same shape as `GET /v1/api/commerce/carts/{id}`, now including any merged-in lines, with status 200.

### Parameters

**Path**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (UUID) | yes | Cart id. Must be an `active` cart. |

**Headers**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `X-Customer-Token` | string | yes | Customer session token. Must resolve to a customer, else 401. |

No body.

```bash title="Request"
curl -X POST https://api.borkol.com/v1/api/commerce/carts/9d3f2a10-6b7c-4e6d-9f21-8a54c1d2e3f4/claim \
  -H "Authorization: Bearer {api_key}" \
  -H "X-Customer-Token: {customer_token}"
```

```javascript title="Request (JS)"
const res = await fetch(
  `https://api.borkol.com/v1/api/commerce/carts/${cartId}/claim`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "X-Customer-Token": customerToken,
    },
  }
);
const { data: cart } = await res.json();
```

```json title="Response"
{
  "data": {
    "id": "9d3f2a10-6b7c-4e6d-9f21-8a54c1d2e3f4",
    "status": "active",
    "lines": [
      { "id": "5e6f7a8b-...", "quantity": 3, "...": "..." },
      { "id": "6f7a8b9c-...", "quantity": 1, "...": "merged in from the customer's previous cart" }
    ],
    "...": "same cart shape as GET /v1/api/commerce/carts/{id}"
  }
}
```

### Errors

| Status | Body | When |
| --- | --- | --- |
| 401 | empty body | `X-Customer-Token` missing or unresolvable, or the customers module is unavailable. |
| 403 | empty body | The cart is already bound to a different customer. |
| 404 | `{"message": "..."}` | Cart unknown, expired, or not `active`. |
| 403 | scope, module, or suspension body | See the Carts page. |
| 429 | `{"message": "Too Many Attempts."}` | Rate limit exceeded. |
