# Calculate tax and shipping

URL: https://docs.sparklayer.io/ignite/api/calculate-tax-and-shipping
Operation: `POST /v1/{siteEnv}/{siteId}/checkout/calculate` (operationId `calculateOrder`)
OpenAPI spec: https://docs.sparklayer.io/openapi/ignite.yaml

This endpoint is called at the final step of the checkout process in order to show tax and shipping methods available to the customer.

`POST /v1/{siteEnv}/{siteId}/checkout/calculate`

> This is an Ignite endpoint: your integration service implements it and SparkLayer calls it.

### Request SparkLayer sends

```http
POST /v1/live/bobs-store/checkout/calculate HTTP/1.1
Host: ignite.example.com
Content-Type: application/json

{
  "cart_id": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
  "customer": {
    "id": "cu_1",
    "email": "buyer@example.com",
    "external_id": "CUS1234",
    "accounting_id": "<accounting_id>",
    "default_billing_address_id": "<default_billing_address_id>",
    "default_shipping_address_id": "<default_shipping_address_id>",
    "temporary_external_address_ids": [
      "<temporary_external_address_ids>"
    ],
    "payment_on_account": {
      "credit_limit": 0,
      "balance": 0,
      "currency_code": "<currency_code>",
      "net_terms": "7_days"
    }
  },
  "currency_code": "gbp",
  "billing_address": {
    "title": "Mr",
    "first_name": "Bob",
    "last_name": "Jones",
    "company": "Tom Jones Climbing Ltd",
    "address_line1": "Example Industrial Estate",
    "address_line2": "North Country",
    "city": "Cityland",
    "region_name": "California",
    "region_code": "CA",
    "postal_code": "12345",
    "country_code": "US",
    "phone": "+44 (0) 123456789",
    "is_default_shipping": true,
    "is_default_billing": true,
    "is_temporary": true
  },
  "shipping_address": {
    "title": "Mr",
    "first_name": "Bob",
    "last_name": "Jones",
    "company": "Tom Jones Climbing Ltd",
    "address_line1": "Example Industrial Estate",
    "address_line2": "North Country",
    "city": "Cityland",
    "region_name": "California",
    "region_code": "CA",
    "postal_code": "12345",
    "country_code": "US",
    "phone": "+44 (0) 123456789",
    "is_default_shipping": true,
    "is_default_billing": true,
    "is_temporary": true
  },
  "shipping_line": {
    "type": "platform",
    "id": "<id>",
    "name": "<name>",
    "price": 4.8,
    "tax_rate": 0.2
  },
  "internal_shipping_methods": [
    {
      "title": "<title>",
      "price_v2": {
        "tax_rate": 0,
        "net": 0,
        "gross": 0,
        "currency_code": "<currency_code>"
      },
      "handle": "<handle>",
      "cost_type": "<cost_type>",
      "cost_language_string": "<cost_language_string>"
    }
  ],
  "shop": {
    "base_currency_code": "gbp"
  },
  "line_items": [
    {
      "type": "product",
      "spark_item_key": "00a0c91e6bf600a0c91e6bf",
      "spark_variant_id": "8ac52999-d163-4d68-ae30-a5f0c089ed79",
      "sku": "EXAMPLE-SKU",
      "product_variant_external_id": "PROD123456",
      "product_external_id": "PROD1234",
      "quantity": 10,
      "unit_price": 4.8,
      "custom_attributes": [
        {
          "key": "<key>",
          "value": "<value>"
        }
      ],
      "options": [
        {
          "group": "<group>",
          "value": "<value>"
        }
      ]
    }
  ],
  "ignite_state": {},
  "ignite_checkout_context": {
    "channel_id": 12345
  },
  "shipping_method_source": "sparklayer"
}
```

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `siteEnv` | string | Yes | The SparkLayer environment the request is for, such as `live`. Store it, with the site ID, when SparkLayer connects your platform. Example: `live` |
| `siteId` | string | Yes | The SparkLayer site ID the request is for, such as `bobs-store`. Store it, with the environment, when SparkLayer connects your platform. Length: min 1. Example: `bobs-store` |

### Request body (`application/json`) (required)

Checkout.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `cart_id` | string (uuid) | | SparkLayer Cart ID |
| `customer` | object | | |
| `customer.id` | string | Yes | SparkLayer ID of Customer |
| `customer.email` | string (email) | Yes | |
| `customer.external_id` | string | Yes | eCommerce platform ID of Customer |
| `customer.accounting_id` | string \| null | | |
| `customer.default_billing_address_id` | string \| null | | eCommerce platform ID of default billing address |
| `customer.default_shipping_address_id` | string \| null | | eCommerce platform ID of default shipping address |
| `customer.temporary_external_address_ids` | string[] | | List of temporary (drop shipping) address IDs associated with the customer to be deleted once the order has been completed. Should be the external (platform) address ID |
| `customer.payment_on_account` | object \| null | | |
| `customer.payment_on_account.credit_limit` | number (float) \| null | | Max 999999999.999. |
| `customer.payment_on_account.balance` | number (float) \| null | | Max 999999999.999. |
| `customer.payment_on_account.currency_code` | string \| null | | If set allows overriding the currency for the customer, otherwise the base currency of the store is to be used. Pattern: `^[A-Za-z]{3}$`. |
| `customer.payment_on_account.net_terms` | "7_days" \| "15_days" \| "30_days" \| "45_days" \| "60_days" \| "90_days" \| null | | |
| `currency_code` | string | | [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code. Length: min 3, max 3. |
| `billing_address` | object | | |
| `billing_address.title` | string \| null | | Length: max 30. |
| `billing_address.first_name` | string \| null | | Length: max 50. |
| `billing_address.last_name` | string \| null | | Length: max 50. |
| `billing_address.company` | string \| null | | Length: max 128. |
| `billing_address.address_line1` | string | | Length: max 255. |
| `billing_address.address_line2` | string \| null | | Length: max 255. |
| `billing_address.city` | string | | Length: max 128. |
| `billing_address.region_name` | string \| null | | Length: max 128. |
| `billing_address.region_code` | string \| null | | Length: max 10. |
| `billing_address.postal_code` | string \| null | | Length: max 20. |
| `billing_address.country_code` | string | | Two letter country code as defined by ISO 3166-2. Length: max 2. |
| `billing_address.phone` | string | | Length: max 20. |
| `billing_address.is_default_shipping` | boolean | | |
| `billing_address.is_default_billing` | boolean | | |
| `billing_address.is_temporary` | boolean | | Whether the address is temporary for the duration of a cart. This value is only relevant to platforms that create the order address automatically on order creation (or conversion from draft to normal order as in the case of our Shopify integration). In such cases we will want the integration to ensure this address does not get created in SparkLayer when the platform notifes the integration of a new customer address. |
| `shipping_address` | object | | |
| `shipping_address.title` | string \| null | | Length: max 30. |
| `shipping_address.first_name` | string \| null | | Length: max 50. |
| `shipping_address.last_name` | string \| null | | Length: max 50. |
| `shipping_address.company` | string \| null | | Length: max 128. |
| `shipping_address.address_line1` | string | | Length: max 255. |
| `shipping_address.address_line2` | string \| null | | Length: max 255. |
| `shipping_address.city` | string | | Length: max 128. |
| `shipping_address.region_name` | string \| null | | Length: max 128. |
| `shipping_address.region_code` | string \| null | | Length: max 10. |
| `shipping_address.postal_code` | string \| null | | Length: max 20. |
| `shipping_address.country_code` | string | | Two letter country code as defined by ISO 3166-2. Length: max 2. |
| `shipping_address.phone` | string | | Length: max 20. |
| `shipping_address.is_default_shipping` | boolean | | |
| `shipping_address.is_default_billing` | boolean | | |
| `shipping_address.is_temporary` | boolean | | Whether the address is temporary for the duration of a cart. This value is only relevant to platforms that create the order address automatically on order creation (or conversion from draft to normal order as in the case of our Shopify integration). In such cases we will want the integration to ensure this address does not get created in SparkLayer when the platform notifes the integration of a new customer address. |
| `shipping_line` | ShippingLine \| ShippingLinePlatformIdOnly \| null | | If using platform shipping, on the first load of the final checkout screen, this will be `null`, after it will be an ID from a method from the response of `available_shipping_methods` If using SparkLayer shipping, this can be `null` if none found or will be the method. |
| `internal_shipping_methods` | object[] \| null | | **Deprecated.** List of available SparkLayer shipping methods for the given checkout, if not using spark shipping this will be empty. |
| `internal_shipping_methods[].title` | string | | |
| `internal_shipping_methods[].price_v2` | object | | |
| `internal_shipping_methods[].price_v2.tax_rate` | number | | |
| `internal_shipping_methods[].price_v2.net` | number | | |
| `internal_shipping_methods[].price_v2.gross` | number | | |
| `internal_shipping_methods[].price_v2.currency_code` | string | | |
| `internal_shipping_methods[].handle` | string | | |
| `internal_shipping_methods[].cost_type` | string | | |
| `internal_shipping_methods[].cost_language_string` | string \| null | | |
| `shop` | object | | |
| `shop.base_currency_code` | string | | [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code. Length: min 3, max 3. |
| `line_items` | (LineItemProduct \| LineItemCustom)[] | | |
| `line_items[].type` | "product" | | |
| `line_items[].spark_item_key` | string | | Length: max 32. |
| `line_items[].spark_variant_id` | string | | Length: max 36. |
| `line_items[].sku` | string | | |
| `line_items[].product_variant_external_id` | string | | eCommerce platform ID of Product Variant |
| `line_items[].product_external_id` | string | | eCommerce platform ID of Product |
| `line_items[].quantity` | integer | | |
| `line_items[].unit_price` | number | | Net price of a unit |
| `line_items[].custom_attributes` | object[] | | Custom attributes allow arbitrary data to be attached to a line item |
| `line_items[].custom_attributes[].key` | string | | |
| `line_items[].custom_attributes[].value` | string | | |
| `line_items[].options` | object[] \| null | | |
| `line_items[].options[].group` | string | | |
| `line_items[].options[].value` | string | | |
| `line_items[].name` | string | | Length: max 128. |
| `ignite_state` | object | | Context which was set by a previous response from /checkout/calculate |
| `ignite_checkout_context` | object \| null | | Extra context provided by the checkout frontend. This allows passing arbitrary platform-specific data that might be needed by the platform's checkout process. For example, for BigCommerce, this could be used to pass a `channel_id` from the store theme to the ignite integration in order to support multi-channel stores. |
| `shipping_method_source` | "sparklayer" \| "platform" | | Specifies the store's configured source for retrieving shipping methods during checkout. When `sparklayer`, a `null` `shipping_line` means the cart has no shipping charge, and no platform rate is fetched or applied. Absent is treated as `platform`. |

```json
{
  "cart_id": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
  "customer": {
    "id": "cu_1",
    "email": "buyer@example.com",
    "external_id": "CUS1234",
    "accounting_id": "<accounting_id>",
    "default_billing_address_id": "<default_billing_address_id>",
    "default_shipping_address_id": "<default_shipping_address_id>",
    "temporary_external_address_ids": [
      "<temporary_external_address_ids>"
    ],
    "payment_on_account": {
      "credit_limit": 0,
      "balance": 0,
      "currency_code": "<currency_code>",
      "net_terms": "7_days"
    }
  },
  "currency_code": "gbp",
  "billing_address": {
    "title": "Mr",
    "first_name": "Bob",
    "last_name": "Jones",
    "company": "Tom Jones Climbing Ltd",
    "address_line1": "Example Industrial Estate",
    "address_line2": "North Country",
    "city": "Cityland",
    "region_name": "California",
    "region_code": "CA",
    "postal_code": "12345",
    "country_code": "US",
    "phone": "+44 (0) 123456789",
    "is_default_shipping": true,
    "is_default_billing": true,
    "is_temporary": true
  },
  "shipping_address": {
    "title": "Mr",
    "first_name": "Bob",
    "last_name": "Jones",
    "company": "Tom Jones Climbing Ltd",
    "address_line1": "Example Industrial Estate",
    "address_line2": "North Country",
    "city": "Cityland",
    "region_name": "California",
    "region_code": "CA",
    "postal_code": "12345",
    "country_code": "US",
    "phone": "+44 (0) 123456789",
    "is_default_shipping": true,
    "is_default_billing": true,
    "is_temporary": true
  },
  "shipping_line": {
    "type": "platform",
    "id": "<id>",
    "name": "<name>",
    "price": 4.8,
    "tax_rate": 0.2
  },
  "internal_shipping_methods": [
    {
      "title": "<title>",
      "price_v2": {
        "tax_rate": 0,
        "net": 0,
        "gross": 0,
        "currency_code": "<currency_code>"
      },
      "handle": "<handle>",
      "cost_type": "<cost_type>",
      "cost_language_string": "<cost_language_string>"
    }
  ],
  "shop": {
    "base_currency_code": "gbp"
  },
  "line_items": [
    {
      "type": "product",
      "spark_item_key": "00a0c91e6bf600a0c91e6bf",
      "spark_variant_id": "8ac52999-d163-4d68-ae30-a5f0c089ed79",
      "sku": "EXAMPLE-SKU",
      "product_variant_external_id": "PROD123456",
      "product_external_id": "PROD1234",
      "quantity": 10,
      "unit_price": 4.8,
      "custom_attributes": [
        {
          "key": "<key>",
          "value": "<value>"
        }
      ],
      "options": [
        {
          "group": "<group>",
          "value": "<value>"
        }
      ]
    }
  ],
  "ignite_state": {},
  "ignite_checkout_context": {
    "channel_id": 12345
  },
  "shipping_method_source": "sparklayer"
}
```

### Responses your service returns

#### 200: Returns tax and shipping information

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `available_shipping_methods` | object[] | | |
| `available_shipping_methods[].id` | string | | |
| `available_shipping_methods[].name` | string | | |
| `available_shipping_methods[].price` | number | | Net of tax |
| `available_shipping_methods[].currency_code` | string | | [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code. Length: min 3, max 3. |
| `platform_shipping_line` | object | | The shipping line applied to the order in the eCommerce platform. Determines price and tax amounts for SparkLayer. |
| `platform_shipping_line.type` | "platform" \| "sparklayer" | | |
| `platform_shipping_line.id` | string | | |
| `platform_shipping_line.name` | string | | |
| `platform_shipping_line.price` | number | | Net of tax |
| `platform_shipping_line.tax_rate` | number | | |
| `totals` | object | | |
| `totals.line_items` | number | | Sub-total of line items (Nett of tax) |
| `totals.shipping_price` | number | | Shipping cost (Nett of tax) |
| `totals.tax` | number | | Tax for the order |
| `totals.total_price` | number | | Order total (Gross of tax) |
| `ignite_state` | object | | Context which will be provided in subsequent requests to /checkout/complete |

```json
{
  "available_shipping_methods": [
    {
      "id": "next-day",
      "name": "Next day delivery",
      "price": 4.8,
      "currency_code": "gbp"
    }
  ],
  "platform_shipping_line": {
    "type": "platform",
    "id": "<id>",
    "name": "<name>",
    "price": 4.8,
    "tax_rate": 0.2
  },
  "totals": {
    "line_items": 14.8,
    "shipping_price": 4.8,
    "tax": 2.2,
    "total_price": 21.8
  },
  "ignite_state": {}
}
```

#### 400: Invalid state/configuration caused the request to fail

`application/problem+json`: the standard error body (below).

#### 401: Auth Failure

#### 500: System Exception

### Error body

Error responses with a body use this RFC 7807 problem details object. See [Errors](https://docs.sparklayer.io/developers/errors.md) for every status code and which errors to retry.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `detail` | string | | Human-readable detail of the error |
| `status` | integer | | HTTP Status code returned from API |
| `title` | string | | Human-readable summary of the error |
| `type` | string | | |
| `errors` | object[] \| null | | A list of errors related to the call |
| `errors[].code` | string | | Machine-readable error code |
| `errors[].property` | string | | The property the error relates to |
| `errors[].message` | string | | Human readable summary of the error |

```json
{
  "detail": "Data Validation Failed",
  "status": 400,
  "title": "invalid-api-request-contents",
  "type": "invalid-api-request-contents",
  "errors": [
    {
      "code": "resource-not-found",
      "property": "stock_location_id",
      "message": "stock_location_id not found"
    }
  ]
}
```
