# Shipping methods

Shipping methods are configured by the merchant. Each has a fixed `amount` and an optional `free_over_amount` threshold: when the cart's goods gross total reaches the threshold, shipping is free for that cart. This endpoint returns each method with its `effective_amount` already resolved against the given cart, so the storefront can render the shipping picker without re-implementing the free-shipping rule.

Shipping only applies to carts that contain shippable lines. Product types `simple`, `variable`, and `configurable` are shippable; `digital`, `license`, and `subscription` are not. See the Checkout page for when `shipping_method_id` is required.

## GET /v1/api/commerce/carts/{id}/shipping-methods

Lists the store's active shipping methods, ordered by name, with the effective price for this cart. Requires `commerce:read`. Cart ownership is enforced (see the Carts page). The list is not paginated.

### Parameters

**Path**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (UUID) | yes | Cart id. Any status is accepted for lookup, but the price context only makes sense for an `active` cart. |

**Headers**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `X-Customer-Token` | string | no | Required if the cart is bound to a non-guest customer. |

### Response fields

The envelope is `{"data": [...]}`. Unlike cart and order money values, the amounts here are bare integers in minor units, not money objects; the currency is the cart's currency (`EUR`).

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (UUID) | always | Shipping method id. Pass it as `shipping_method_id` at checkout. |
| `name` | string | always | Display name. |
| `amount` | integer | always | Base price in minor units. |
| `free_over_amount` | integer or null | always | Goods gross total (minor units) at which shipping becomes free, or null when the method is never free. |
| `effective_amount` | integer | always | The price this cart would pay right now: 0 when the cart's goods gross total reaches `free_over_amount`, otherwise `amount`. |

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

```javascript title="Request (JS)"
const res = await fetch(
  `https://api.borkol.com/v1/api/commerce/carts/${cartId}/shipping-methods`,
  { headers: { Authorization: `Bearer ${apiKey}` } }
);
const { data: methods } = await res.json();
```

```json title="Response"
{
  "data": [
    {
      "id": "3c4d5e6f-7a8b-4c9d-0e1f-2a3b4c5d6e7f",
      "name": "Express",
      "amount": 995,
      "free_over_amount": null,
      "effective_amount": 995
    },
    {
      "id": "4d5e6f7a-8b9c-4d0e-1f2a-3b4c5d6e7f8a",
      "name": "Standard",
      "amount": 495,
      "free_over_amount": 5000,
      "effective_amount": 0
    }
  ]
}
```

`effective_amount` is a snapshot for the cart as it is now: changing cart contents can change it, so re-fetch after cart mutations if the shipping picker is visible.

### Errors

| Status | Body | When |
| --- | --- | --- |
| 404 | `{"message": "..."}` | Unknown cart id or a cart of another store. |
| 403 | `{"message": "This cart belongs to a customer."}` | Ownership check failed. |
| 403 | scope, module, or suspension body | See the Carts page. |
| 429 | `{"message": "Too Many Attempts."}` | Rate limit exceeded. |
