# Create a tax configuration

Operation ID: `createTaxConfiguration`

Group: Accounting › Tax Configurations

[Open in the interactive reference](https://docs.api.lensing.ai/lensing/v1/endpoints/createTaxConfiguration)

`POST /accounting/tax-configurations`

Retry-safe: A write that accepts Idempotency-Key: repeating a request with the same key replays the stored answer instead of executing twice.

Creates a tax configuration for a legal entity. A legal entity has at most one tax configuration. If the legal entity in entityId already has one, the request fails with ENTITY_ALREADY_CONFIGURED, which gives the existing configuration's id.

The response contains only the new configuration's id. Read the configuration with GET accounting/tax-configurations/:id.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Api-Version` | header | string · v1 | yes | The API version this request targets, named by its public identifier. Required on every request; omitting it, or naming a version this company does not have, is answered with 400. This document describes version `v1`. |
| `Idempotency-Key` | header | string | no | An opaque caller-chosen string (1-255 characters) that makes this request safe to retry. Repeating the same key with an identical request body replays the original response instead of re-executing - but only once that original request succeeded; a failed attempt re-executes on retry. Repeating the key with a different body is answered with 422. A key still in flight is answered with 409 - as is a key whose original request was interrupted before it finished, for a few minutes afterwards, until that attempt is presumed dead. A completed response stays replayable for 24 hours. The key is scoped to the caller, not to the action: reusing it for a different action is answered with 409 or 422 rather than treated independently. On read actions this header is ignored. On write actions that cannot offer the guarantee it is answered with `400 IDEMPOTENCY_NOT_SUPPORTED` rather than silently ignored. |

## Request body

Required, `application/json`: createTaxConfigurationInput.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `entityId` | string | yes | The legal entity this configuration belongs to. An entity has at most one tax configuration. See id on GET base/entities. Example: `"b6b3a6ce-6c66-4d43-9c8e-6c6c9b8f4a11"`. |
| `status` | any | yes | The status of the tax configuration. The tax codes of an inactive configuration are not offered on documents. One of: `"Active"`, `"Inactive"`. Example: `"Active"`. |
| `taxRegime` | any | no | The tax system that determines which tax components are valid. Omit to derive it from the entity's country. An unrecognized country derives EU VAT. One of: `"EU_VAT"`, `"India_GST"`, `"Canada_GST"`, `"Ghana_VAT"`, `"SingleRate_GST"`, `"Mexico_IVA"`. Example: `"EU_VAT"`. |
| `lines` | array<object> | yes | The complete set of tax codes for this configuration. Must not be empty. Each line's title, and any taxCode supplied, must be unique within the array. Example: `[{"title":"Standard rate","taxCode":"V19EUG","percentage":"0.19","rateType":"Standard","scope":"Domestic","direction":"Output","isReverseCharge":false,"isActive":true,"accounts":[{"chartOfAccountsId":"1b9c1f2e-4a6d-4c31-9f5e-2d7a08b6c410","accountId":"7e4d3c21-9b08-4a5f-8c62-1f0e5d9a3b74"}]}]`. |
| `lines[].taxCode` | string | no | The business tax code as printed on source documents, for example 'V19EUG' or 'U00EU'. Supply it when migrating from another system. |
| `lines[].title` | string | yes | The display title of the tax code. Must be unique within the configuration. |
| `lines[].comment` | string | no | A free-form note on when to use this tax code. |
| `lines[].vatDeclarationField` | string | no | The box or boxes on the VAT return that this code feeds, taken verbatim from the tax authority form, for example '66' or '46+47+67'. Stored as free text and not evaluated. |
| `lines[].percentage` | decimal | yes | The tax rate, as a decimal fraction. For example, 0.19 is 19%. Send 0 for a zero-rated or exempt code. |
| `lines[].rateType` | any | yes | The legal rate classification of the tax code. One of: `"Standard"`, `"Reduced"`, `"SuperReduced"`, `"Zero"`, `"Exempt"`. |
| `lines[].scope` | any | yes | The territorial scope of the transaction. One of: `"Domestic"`, `"IntraEU_B2B"`, `"IntraEU_B2C"`, `"ExtraEU_Goods"`, `"ExtraEU_Services"`. |
| `lines[].direction` | any | yes | Which side the tax sits on. 'Input' is tax paid on purchases, 'Output' is tax charged on sales. One of: `"Input"`, `"Output"`. |
| `lines[].isReverseCharge` | boolean | yes | Whether the recipient rather than the supplier accounts for the tax, for example under §13b UStG. When true, every account on the line must set reverseChargeCreditAccountId. |
| `lines[].cashBasis` | boolean | no | Cash basis: the tax becomes creditable only when the bill is paid. It posts to the uncollected account and moves to the collected account on payment. Input tax only. Requires components whose accounts all set collectedAccountId. |
| `lines[].isActive` | boolean | yes | Whether the tax code is offered for selection on new documents. |
| `lines[].accounts` | array<object> | yes | The general ledger accounts this tax code books to, usually one entry for each chart of accounts the entity books in. Must not be empty, including for exempt and zero-rated codes, which book a tax line at zero. |
| `lines[].accounts[].chartOfAccountsId` | string | yes | The chart of accounts this mapping applies to. See id on GET accounting/chart-of-accounts/:id. |
| `lines[].accounts[].accountId` | string | yes | The account the tax amount is booked to. See id on GET accounting/accounts/:id. |
| `lines[].accounts[].reverseChargeCreditAccountId` | string | no | The account credited with the offsetting tax amount. Required on reverse-charge lines, ignored otherwise. See id on GET accounting/accounts/:id. |
| `lines[].components` | array<object> | no | The tax components of this code, for example a federal and a state part. Required when cashBasis is true. The components' percentages must add up to the line's percentage. |
| `lines[].components[].taxComponentCode` | string | yes | The tax component code, for example 'VAT'. |
| `lines[].components[].percentage` | decimal | yes | The component's rate as a decimal fraction, for example 0.16 for 16%. |
| `lines[].components[].isRecoverable` | boolean | yes | Whether the tax can be reclaimed. Every component of a cash-basis code must set true. |
| `lines[].components[].accounts` | array<object> | yes | The GL accounts for this component, one entry per chart of accounts the entity books in. Must not be empty. |
| `lines[].components[].accounts[].chartOfAccountsId` | string | yes | The chart of accounts this mapping applies to. See id on GET accounting/chart-of-accounts/:id. |
| `lines[].components[].accounts[].accountId` | string | yes | The account the tax amount is booked to. On a cash-basis code this is the uncollected account. See id on GET accounting/accounts/:id. |
| `lines[].components[].accounts[].reverseChargeCreditAccountId` | string | no | The account credited with the offsetting tax amount on a reverse-charge code. See id on GET accounting/accounts/:id. |
| `lines[].components[].accounts[].collectedAccountId` | string | no | Cash basis only. The account the tax moves to once the bill is paid. Required on every account of a cash-basis component, and must differ from accountId. See id on GET accounting/accounts/:id. |

```json
{
  "entityId": "b6b3a6ce-6c66-4d43-9c8e-6c6c9b8f4a11",
  "status": "Active",
  "taxRegime": "EU_VAT",
  "lines": [
    {
      "title": "Standard rate",
      "taxCode": "V19EUG",
      "percentage": "0.19",
      "rateType": "Standard",
      "scope": "Domestic",
      "direction": "Output",
      "isReverseCharge": false,
      "isActive": true,
      "accounts": [
        {
          "chartOfAccountsId": "1b9c1f2e-4a6d-4c31-9f5e-2d7a08b6c410",
          "accountId": "7e4d3c21-9b08-4a5f-8c62-1f0e5d9a3b74"
        }
      ]
    }
  ]
}
```

## Response

`200` Success

Under `data` (createTaxConfigurationOutput):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | The unique identifier for the created tax configuration. |

```json
{
  "data": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  },
  "diagnostics": [
    {
      "code": "<code>",
      "message": "<message>",
      "severity": "error",
      "path": [
        "<path>"
      ],
      "details": "<details>"
    }
  ]
}
```

## Response headers

- `Idempotency-Replayed` — Present and `true` only when this response replays a previously stored one for the same Idempotency-Key, rather than reflecting a live execution. Absent on a live response - never sent as `false`.

## Errors

| Code | Status |
| --- | --- |
| `ENTITY_NOT_FOUND` | 400 |
| `ENTITY_ALREADY_CONFIGURED` | 400 |
| `NO_LINES` | 400 |
| `DUPLICATE_LINE_TITLE` | 400 |
| `DUPLICATE_TAX_CODE` | 400 |
| `LINE_WITHOUT_ACCOUNTS` | 400 |
| `MISSING_REVERSE_CHARGE_ACCOUNT` | 400 |
| `CHART_OF_ACCOUNTS_NOT_FOUND` | 400 |
| `ACCOUNT_NOT_FOUND` | 400 |
| `REVERSE_CHARGE_ACCOUNT_NOT_FOUND` | 400 |
| `COLLECTED_ACCOUNT_NOT_FOUND` | 400 |
| `COMPONENT_WITHOUT_ACCOUNTS` | 400 |
| `COMPONENT_PERCENTAGE_MISMATCH` | 400 |
| `CASH_BASIS_INVALID` | 400 |
| `IDEMPOTENCY_KEY_CONFLICT` | 409 |
| `IDEMPOTENCY_PAYLOAD_MISMATCH` | 422 |

## Statuses

- `400` — Invalid request or business failure. Codes: ENTITY_NOT_FOUND, ENTITY_ALREADY_CONFIGURED, NO_LINES, DUPLICATE_LINE_TITLE, DUPLICATE_TAX_CODE, LINE_WITHOUT_ACCOUNTS, MISSING_REVERSE_CHARGE_ACCOUNT, CHART_OF_ACCOUNTS_NOT_FOUND, ACCOUNT_NOT_FOUND, REVERSE_CHARGE_ACCOUNT_NOT_FOUND, COLLECTED_ACCOUNT_NOT_FOUND, COMPONENT_WITHOUT_ACCOUNTS, COMPONENT_PERCENTAGE_MISMATCH, CASH_BASIS_INVALID
- `401` — The request carries no valid access token.
- `403` — Not authorized
- `404` — Not found
- `409` — The Idempotency-Key is still being processed by another request.
- `422` — The Idempotency-Key was already used with a different request payload.
- `429` — Too many requests from this client address. The response has no body and no `Retry-After` header.
- `500` — Internal server error
