Introducing our brand new Rules Engine —
Read the docs
LogoLogo
Core APIOther APIsChangelog
Getting started
Getting started
  • Welcome to Commerce Layer
    • Guided setup
    • Manual configuration
  • API specification
  • API credentials
  • Authentication
    • Client credentials
    • Password
    • Authorization code
    • Refresh token
    • JWT bearer
    • Revoking a token
  • Roles and permissions
  • Fetching resources
  • Fetching relationships
  • Including associations
  • Sparse fieldsets
  • Sorting results
  • Pagination
  • Filtering data
  • Creating resources
  • Updating resources
  • Tagging resources
  • Deleting resources
  • Importing resources
  • Exporting resources
  • Cleaning up resources
  • External resources
    • External order validation
    • External prices
    • External shipping costs
    • External payment gateways
    • External promotions
    • External tax calculators
  • Rate limits
  • Handling errors
  • Real-time webhooks
  • Callbacks security
On this page
  • Taxes data granularity
  • Values precedence
  • Request
  • Response
  • Security
  1. External resources

External tax calculators

How to manage custom tax calculation via external services

PreviousExternal promotionsNextRate limits

Last updated 29 days ago

Commerce Layer supports automatic tax calculation thanks to the out-of-the-box integration with , , , and . can be also configured. On top of that, you also have the complete flexibility to implement any particular custom tax calculation logic building your own .

Whenever the tax calculation is fired, Commerce Layer triggers a POST request to the tax_calculator_url endpoint, sending the order payload (including its line items and their items) in the request body. To trigger the tax update, the order must be not archived and have the following attributes:

  • a valid shipping_address or a valid billing_address

  • an external_tax_calculator associated with the market in scope

  • a positive total_amount_cent

  • at least one SKU among its line item (gift cards are not taxed)

Taxes data granularity

External taxes can be applied either to the whole order or to specific line items, depending on how granular your response is. If you need to pass tax values at the line item level and set tax breakdown attributes, you need to include in the response an array containing the line items you want to be affected by the tax update and specify each line item ID, as the payload passed in the request to your external calculator.

Values precedence

When designing your external tax calculator response, bear in mind that some values take precedence over other ones, according to the following logic.

1. Line item tax collectable

If the tax_collectable attribute is specified for a line item, its value is used to compute the tax_amount_cents for the line item, no matter what's the value of the line item tax_rate (if any).

The tax_collectable attribute is a float (e.g. "tax_collectable": 9.0 sets the line item tax_amount_cents to 900).

2. Line item tax rate

If the tax_collectable is missing for a line item, the line item tax_amount_cents is computed using the line item tax_rate.

3. Global tax rate

If both the line item tax_collectable and tax_rate are missing, the line item tax_amount_cents is computed using the tax_rate specified at the data level. If the line items array is missing, that tax rate is applied to the whole order.

Request

  • market

  • customer

  • line_items

  • line_items.item

  • shipping_address

  • billing_address

{
  "data": {
    "id": "wBXVhKzrnq",
    "type": "orders",
    "links": { ... },
    "attributes": { ... },
    "relationships": { ... },
    "meta": { ... }
  },
  "included": [
    {
      "id": "DvlGRmhdgX",
      "type": "markets",
      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    {
      "id": "DHvfpESrCx",
      "type": "customers",
      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    {
      "id": "kdPgtRXOKL",
      "type": "line_items",
      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    {
      "id": "XGZwpOSrWL",
      "type": "skus",
      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    {
      "id": "BgnguJvXmb",
      "type": "addresses",
      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    {
      "id": "AlrkugwyVW",
      "type": "addresses",
      "links": { ... },
      "attributes": { ... },
      "relationships": { ... },
      "meta": { ... }
    },
    { ... }
  ]
}

Response

Your service response (or error) must match the format described in the examples below.

Applying the same tax rate to the whole order

The successful response must be a JSON object, returning at least the tax rate to be applied to the whole order. If needed, you can optionally enrich the response with some additional information, notification messages, and metadata:

{
  "success": true,
  "data": {
    "tax_rate": 0.25,
    "messages": [ ... ],
    "metadata": {
      "foo": "bar"
    }
  }
}

On error, you must respond with an HTTP code >= 400. The response body must be a JSON object containing any other relevant error code and message:

{
  "success": false,
  "error": {
    "code": "YOUR-ERROR-CODE",
    "message": "Your error message"
  }
}

Applying different tax rates for some line items

The successful response must be a JSON object, returning at least the specific tax rate to be applied to the line items. If needed, you can optionally enrich the response with some additional information, notification messages, and metadata:

{
  "success": true,
  "data": {
    "tax_rate": 0.25,
    "line_items": [
      {
        "id": "kxnXtEaGxo",
        "tax_rate": 0.3
      },
      {
        "id": "kXBqtrgARW",
        "tax_rate": 0.4
      }
    ],
    "messages": [ ... ],
    "metadata": {
      "foo": "bar"
    }
  }
}

On error, you must respond with an HTTP code >= 400. The response body must be a JSON object containing any other relevant error code and message:

{
  "success": false,
  "error": {
    "code": "YOUR-ERROR-CODE",
    "message": "Your error message"
  }
}

More complex external tax calculations affecting the tax breakdown

The successful response must be a JSON object, returning at least specific tax collectable and tax rate values to be applied to the line items, and some attributes you want to set in the line items tax breakdown. If needed, you can optionally enrich the response with some additional information, notification messages, and metadata:

{
  "success": true,
  "data": {
    "freight_taxable": true,
    "tax_rate": 0.25,
    "line_items": [
      {
        "id": "kxnXtEaGxo",
        "tax_collectable": 2.25,
        "country_tax_collectable": 9,
        "country_tax_rate": 0.2,
        "country_taxable_amount": 9,
        "special_tax_rate": 0.05,
        "taxable_amount": 10,
        "tax_rate": 0.4
      },
      {
        "id": "kXBqtrgARW",
        "taxable_amount": 12,
        "tax_rate": 0.3
      }
    ],
    "messages": [ ... ]
    "metadata": {
      "foo": "bar"
    }
  }
}

On error, you must respond with an HTTP code >= 400. The response body must be a JSON object containing any other relevant error code and message:

{
  "success": false,
  "error": {
    "code": "YOUR-ERROR-CODE",
    "message": "Your error message"
  }
}

Security

When you create a new external tax calculator, a shared secret is generated. We recommend verifying the callback authenticity by signing the payload with that shared secret and comparing the result with the callback signature header.

The request payload is a compliant object you can query to perform your own computation. Aside from the target resource — orders in the specific case — some relationships are also included to avoid useless API roundtrips:

In case the call to your external endpoint goes timeout, responds with an internal server error, or is blocked by the open , the API keeps responding with a 200 OK status code. No tax amount is updated on the order and the circuit breaker internal counter (if closed) is incremented.

If you specify the messages key a with the related information is automatically attached to the associated order.

JSON:API
Callbacks security
Manual tax calculators
external tax calculator
Avalara
Stripe Tax
TaxJar
Vertex
circuit breaker
notification