StabisoDocs

API reference

Sales tax

Operation Summary Scope Idempotency-Key
GET /v1/entities/{entity}/customers/{party}/sales-tax Get a customer's tax area and exemption read
PUT /v1/entities/{entity}/customers/{party}/sales-tax Set a customer's tax area and exemption draft
GET /v1/entities/{entity}/sales-tax Get the sales tax setup read
POST /v1/entities/{entity}/sales-tax/areas Add a tax area admin
PATCH /v1/entities/{entity}/sales-tax/areas/{area} Change a tax area admin
PUT /v1/entities/{entity}/sales-tax/category-rules Set how much of a product category a state taxes admin
POST /v1/entities/{entity}/sales-tax/jurisdictions Add a county, city or district admin
POST /v1/entities/{entity}/sales-tax/jurisdictions/{jurisdiction}/rates Set a jurisdiction's rate from a day admin
GET /v1/entities/{entity}/sales-tax/periods List the sales tax return periods of a year read
POST /v1/entities/{entity}/sales-tax/registrations Register the company in a state admin required
PATCH /v1/entities/{entity}/sales-tax/registrations/{state} Change a state registration admin
POST /v1/entities/{entity}/sales-tax/returns Record a sales tax return as filed draft required
GET /v1/entities/{entity}/sales-tax/returns/{state}/{period} Get a state's sales tax return read
GET /v1/products/{product_id}/sales-tax Get a product's sales tax category read
PUT /v1/products/{product_id}/sales-tax Set a product's sales tax category admin
POST /v1/sales-tax-returns/{sales_tax_return_id}/approve Approve a sales tax filing draft required
GET /v1/sales-tax-returns/{sales_tax_return_id}/payment-candidates List the entries that may have paid a sales tax return read
POST /v1/sales-tax-returns/{sales_tax_return_id}/payments Link the entry that paid a sales tax return draft required
POST /v1/sales-tax-returns/{sales_tax_return_id}/payments/{sales_tax_payment_id}/unlink Unlink a payment from a sales tax return draft required
POST /v1/sales-tax-returns/{sales_tax_return_id}/withdraw Withdraw a sales tax filing draft required

GET /v1/entities/{entity}/customers/{party}/sales-tax

Get a customer's tax area and exemption

The customer's state, tax area and exemption certificate, and whether it is valid today.

Required scope read (admin implies every scope)
Operation id get_customer_v1_entities__entity__customers__party__sales_tax_get

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path party string yes Customer code, e.g. C-DE, or its id. A vendor is not a customer: its statement is GET .../vendors/{party}/statement.

Example request

curl "$ERP_URL/v1/entities/CH01/customers/C-DE/sales-tax" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK SalesTaxCustomerOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "party": "string",
  "state": "string",
  "area": "string",
  "area_name": "string",
  "exemption_number": "string",
  "exemption_reason": "government",
  "exempt_from": "2026-09-21",
  "exempt_until": "2026-09-21",
  "exempt_states": [
    "string"
  ],
  "exempt_today": true
}

PUT /v1/entities/{entity}/customers/{party}/sales-tax

Set a customer's tax area and exemption

Replaces the customer's tax area (one of its state) and exemption certificate: with a certificate valid on a document's day, the customer is charged no sales tax. Needs the policy action change_party_details, which agents are always denied (403).

Required scope draft (admin implies every scope)
Idempotency-Key not used
Operation id put_customer_v1_entities__entity__customers__party__sales_tax_put

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path party string yes Customer code, e.g. C-DE, or its id. A vendor is not a customer: its statement is GET .../vendors/{party}/statement.

Request body

application/json (required): SalesTaxCustomerInput

Field Type Required Description
area string or null The tax area of the customer's location (its code), or null.
exemption_number string or null The exemption certificate's number, or null. (max length 60)
exemption_reason "government" | "manufacturing" | "nonprofit" | "other" | "resale" or null Why it is exempt: resale, nonprofit, government, manufacturing or other; required with a number.
exempt_from string (date) or null The first day the certificate is valid; with a number, the day it is recorded when omitted.
exempt_until string (date) or null The certificate's expiry; null when it does not expire.
exempt_states array of string or null The states the certificate is valid in (e.g. a multistate certificate); with a number, the customer's own state when omitted.

Example request

curl -X PUT "$ERP_URL/v1/entities/CH01/customers/C-DE/sales-tax" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "area": "LOS-ANGELES",
  "exempt_from": "2026-01-01",
  "exempt_states": [
    "US-CA"
  ],
  "exempt_until": "2027-12-31",
  "exemption_number": "SR-FH-123456",
  "exemption_reason": "resale"
}'

Responses

Status Meaning Body
200 OK SalesTaxCustomerOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "party": "string",
  "state": "string",
  "area": "string",
  "area_name": "string",
  "exemption_number": "string",
  "exemption_reason": "government",
  "exempt_from": "2026-09-21",
  "exempt_until": "2026-09-21",
  "exempt_states": [
    "string"
  ],
  "exempt_today": true
}

GET /v1/entities/{entity}/sales-tax

Get the sales tax setup

A US company's states it is registered in (with each state's liability account and what it owes now), its jurisdictions and their rates (the one in force on on), its tax areas with their combined rates, and the product taxability it recorded per state. applies is false for a company outside the US, which charges VAT.

Required scope read (admin implies every scope)
Operation id get_setup_v1_entities__entity__sales_tax_get

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
query on string (date) or null The day the rates in force are shown for; default today.

Example request

curl "$ERP_URL/v1/entities/CH01/sales-tax" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK SalesTaxSetupOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "entity": "string",
  "applies": true,
  "on": "2026-09-21",
  "categories": [
    "clothing"
  ],
  "registrations": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "state": "string",
      "state_name": "string",
      "permit_number": "string",
      "registered_from": "2026-09-21",
      "registered_until": "2026-09-21",
      "filing_frequency": "month",
      "account": "string",
      "account_name": "string",
      "use_tax_area": "string",
      "balance": "string"
    }
  ],
  "jurisdictions": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "state": "string",
      "code": "string",
      "name": "string",
      "level": "city",
      "rate": "string",
      "rates": [
        null
      ]
    }
  ],
  "areas": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "state": "string",
      "code": "string",
      "name": "string",
      "jurisdictions": [
        "string"
      ],
      "combined_rate": "string"
    }
  ],
  "rules": [
    {
      "state": "string",
      "category": "clothing",
      "taxable_percent": "string",
      "note": "string"
    }
  ]
}

POST /v1/entities/{entity}/sales-tax/areas

Add a tax area

A place's local jurisdictions (county, city, districts) on top of its state; a customer located there gets the area. Answers 422 for a jurisdiction of another state.

Required scope admin (admin implies every scope)
Idempotency-Key not used
Operation id add_area_v1_entities__entity__sales_tax_areas_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).

Request body

application/json (required): SalesTaxAreaInput

Field Type Required Description
state string yes The state, e.g. US-CA. (pattern ^US-[A-Z]{2}$)
code string or null A code of its own, e.g. LOS-ANGELES. Omitted: made from the name (capitals, digits and hyphens, with -2 when taken). (pattern ^[A-Z0-9][A-Z0-9-]{0,39}$)
name string yes Its name. (min length 1; max length 120)
jurisdictions array of string yes The codes of its local jurisdictions (county, city, district) in the same state; the state's own rate always applies on top. (max items 20)

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/sales-tax/areas" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "code": "LOS-ANGELES",
  "jurisdictions": [
    "CA-LA-DIST"
  ],
  "name": "City of Los Angeles",
  "state": "US-CA"
}'

Responses

Status Meaning Body
201 Created SalesTaxAreaOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "state": "string",
  "code": "string",
  "name": "string",
  "jurisdictions": [
    "string"
  ],
  "combined_rate": "string"
}

PATCH /v1/entities/{entity}/sales-tax/areas/{area}

Change a tax area

Renames the area or replaces its local jurisdictions; documents dated from now on use it.

Required scope admin (admin implies every scope)
Idempotency-Key not used
Operation id update_area_v1_entities__entity__sales_tax_areas__area__patch

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path area string yes A tax area's code, e.g. LOS-ANGELES.

Request body

application/json (required): SalesTaxAreaUpdate

Field Type Required Description
name string or null New name. (min length 1; max length 120)
jurisdictions array of string or null The codes of its local jurisdictions. (max items 20)

Example request

curl -X PATCH "$ERP_URL/v1/entities/CH01/sales-tax/areas/{area}" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "jurisdictions": [
    "CA-LA-DIST"
  ]
}'

Responses

Status Meaning Body
200 OK SalesTaxAreaOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "state": "string",
  "code": "string",
  "name": "string",
  "jurisdictions": [
    "string"
  ],
  "combined_rate": "string"
}

PUT /v1/entities/{entity}/sales-tax/category-rules

Set how much of a product category a state taxes

taxable_percent of the price of products of category is taxed in state: 0 exempt, 100 (also when nothing is recorded) fully taxed.

Required scope admin (admin implies every scope)
Idempotency-Key not used
Operation id set_rule_v1_entities__entity__sales_tax_category_rules_put

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).

Request body

application/json (required): SalesTaxCategoryRuleInput

Field Type Required Description
state string yes The state. (pattern ^US-[A-Z]{2}$)
category "clothing" | "digital_goods" | "general" | "groceries" | "medical" | "professional_services" | "saas" yes The product category.
taxable_percent decimal string yes How much of the price the state taxes: 0 exempt, 100 fully taxed, 80 for Texas data processing. (min 0; max 100)
note string or null Why, e.g. the statute. (max length 300)

Example request

curl -X PUT "$ERP_URL/v1/entities/CH01/sales-tax/category-rules" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "category": "saas",
  "state": "US-TX",
  "taxable_percent": "80"
}'

Responses

Status Meaning Body
200 OK SalesTaxCategoryRuleOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "state": "string",
  "category": "clothing",
  "taxable_percent": "string",
  "note": "string"
}

POST /v1/entities/{entity}/sales-tax/jurisdictions

Add a county, city or district

A local jurisdiction of a state with its first rate; a tax area names it to charge it. Answers 409 when the code is taken.

Required scope admin (admin implies every scope)
Idempotency-Key not used
Operation id add_jurisdiction_v1_entities__entity__sales_tax_jurisdictions_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).

Request body

application/json (required): SalesTaxJurisdictionInput

Field Type Required Description
state string yes The state it lies in, e.g. US-CA. (pattern ^US-[A-Z]{2}$)
code string or null A code of its own, e.g. CA-LA-DIST. Omitted: made from the name (capitals, digits and hyphens, with -2 when taken). (pattern ^[A-Z0-9][A-Z0-9-]{0,39}$)
name string yes Its name. (min length 1; max length 120)
level "city" | "county" | "district" yes county, city or district.
rate decimal string yes Its rate in percent. (min 0)
valid_from string (date) yes The first day of the rate.

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/sales-tax/jurisdictions" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "code": "CA-LA-DIST",
  "level": "district",
  "name": "Los Angeles County district taxes",
  "rate": "2.5",
  "state": "US-CA",
  "valid_from": "2025-04-01"
}'

Responses

Status Meaning Body
201 Created SalesTaxJurisdictionOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "state": "string",
  "code": "string",
  "name": "string",
  "level": "city",
  "rate": "string",
  "rates": [
    {
      "valid_from": "2026-09-21",
      "rate": "string",
      "source": "string"
    }
  ]
}

POST /v1/entities/{entity}/sales-tax/jurisdictions/{jurisdiction}/rates

Set a jurisdiction's rate from a day

A new rate from valid_from for documents dated on or after it (a rate of the same day is replaced); posted documents keep the rates they were charged at.

Required scope admin (admin implies every scope)
Idempotency-Key not used
Operation id set_rate_v1_entities__entity__sales_tax_jurisdictions__jurisdiction__rates_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path jurisdiction string yes A jurisdiction's code, e.g. CA-LA-DIST.

Request body

application/json (required): SalesTaxRateInput

Field Type Required Description
valid_from string (date) yes The first day of the rate; a rate of the same day is replaced.
rate decimal string yes The rate in percent. (min 0)
source string or null Where it comes from, e.g. the state's notice. (max length 500)

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/sales-tax/jurisdictions/{jurisdiction}/rates" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "rate": "7.5",
  "valid_from": "2027-01-01"
}'

Responses

Status Meaning Body
200 OK SalesTaxJurisdictionOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "state": "string",
  "code": "string",
  "name": "string",
  "level": "city",
  "rate": "string",
  "rates": [
    {
      "valid_from": "2026-09-21",
      "rate": "string",
      "source": "string"
    }
  ]
}

GET /v1/entities/{entity}/sales-tax/periods

List the sales tax return periods of a year

Each registration's periods of the year that have started, with the tax from the ledger and their state (open, to_file, due_soon, overdue, awaiting_approval, filed, paid) with the day each return is due, and the year's sales into states the company is not registered in.

Required scope read (admin implies every scope)
Operation id list_periods_v1_entities__entity__sales_tax_periods_get

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
query year integer yes The calendar year. (min 2000; max 2100)

Example request

curl "$ERP_URL/v1/entities/CH01/sales-tax/periods?year=value" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK SalesTaxPeriodsOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "entity": "string",
  "year": 1,
  "currency": "string",
  "periods": [
    {
      "state": "string",
      "state_name": "string",
      "period": "string",
      "start": "2026-09-21",
      "end": "2026-09-21",
      "due_on": "2026-09-21",
      "status": "awaiting_approval",
      "tax": "string",
      "tax_due": "string",
      "filed_on": "2026-09-21",
      "return_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    }
  ],
  "unregistered": [
    {
      "state": "string",
      "state_name": "string",
      "sales": "string",
      "documents": 1
    }
  ]
}

POST /v1/entities/{entity}/sales-tax/registrations

Register the company in a state

Records that the company is registered for sales tax in a state from a day (nexus): from then on its sales to customers in that state are taxed. Creates the state's liability account (2200-CA, under the company's sales tax payable) and the state's jurisdiction at the state rate (state_rate, else the base rate data-vNext has). Answers 409 when already registered, 422 for a company outside the US (sales_tax_not_us) or a state data-vNext has no rate for and none was given (sales_tax_state_rate_unknown). Needs the policy action manage_accounts.

Required scope admin (admin implies every scope)
Idempotency-Key required
Operation id register_v1_entities__entity__sales_tax_registrations_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).

Request body

application/json (required): SalesTaxRegistrationInput

Field Type Required Description
state string yes The state, ISO 3166-2, e.g. US-CA. (pattern ^US-[A-Z]{2}$)
permit_number string or null The state's seller's permit or account number. (max length 60)
registered_from string (date) yes The first day tax is charged in the state.
registered_until string (date) or null The last day, when the registration ended; null while in force.
filing_frequency "month" | "quarter" | "year" yes How often the state's return is filed: month, quarter or year.
state_rate decimal string or null The state rate in percent, from registered_from. Omit to take the state base rate data-vNext has (422 sales_tax_state_rate_unknown when it has none). Ignored when the state already has its jurisdiction. (min 0)
use_tax_area string or null In the company's own state: the tax area (its code) where it uses what it buys, whose local rates its use tax is self-assessed at; null for the state rate only.

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/sales-tax/registrations" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "filing_frequency": "quarter",
  "permit_number": "102-345678",
  "registered_from": "2026-01-01",
  "state": "US-CA"
}'

Responses

Status Meaning Body
201 Created SalesTaxRegistrationOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "state": "string",
  "state_name": "string",
  "permit_number": "string",
  "registered_from": "2026-09-21",
  "registered_until": "2026-09-21",
  "filing_frequency": "month",
  "account": "string",
  "account_name": "string",
  "use_tax_area": "string",
  "balance": "string"
}

PATCH /v1/entities/{entity}/sales-tax/registrations/{state}

Change a state registration

Changes the permit number, the last day (registered_until: no tax is charged in the state after it) or the filing frequency. Only the fields sent change.

Required scope admin (admin implies every scope)
Idempotency-Key not used
Operation id update_registration_v1_entities__entity__sales_tax_registrations__state__patch

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path state string yes The state, e.g. US-CA. (pattern ^US-[A-Z]{2}$)

Request body

application/json (required): SalesTaxRegistrationUpdate

Field Type Required Description
permit_number string or null The state's permit number. (max length 60)
registered_until string (date) or null The last day of the registration; null while in force.
filing_frequency "month" | "quarter" | "year" or null How often it files; not changed once a return is recorded as filed (409 sales_tax_frequency_filed).
use_tax_area string or null The use tax area's code; null for the state rate only.

Example request

curl -X PATCH "$ERP_URL/v1/entities/CH01/sales-tax/registrations/{state}" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "registered_until": "2026-12-31"
}'

Responses

Status Meaning Body
200 OK SalesTaxRegistrationOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "state": "string",
  "state_name": "string",
  "permit_number": "string",
  "registered_from": "2026-09-21",
  "registered_until": "2026-09-21",
  "filing_frequency": "month",
  "account": "string",
  "account_name": "string",
  "use_tax_area": "string",
  "balance": "string"
}

POST /v1/entities/{entity}/sales-tax/returns

Record a sales tax return as filed

Records that the state's return for the period was filed with the state on filed_on, with the state's confirmation number, and keeps the figures as they are now; a second person approves the record (POST /v1/sales-tax-returns/{id}/approve). Nothing is sent: the application files no return, and in a test copy (test_copy) the record is local only. Answers 409 when already recorded (withdraw it first), 422 for a day in the future or in the period. A person's act: needs the policy action finalize_vat_return, which agents are always denied (403).

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id file_return_v1_entities__entity__sales_tax_returns_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).

Request body

application/json (required): SalesTaxFileInput

Field Type Required Description
state string yes The state, e.g. US-TX. (pattern ^US-[A-Z]{2}$)
period string yes The period by the registration's frequency: 2026-08, 2026-Q3, 2026. (pattern `^\d{4}(-(0[1-9]
filed_on string (date) yes The day it was filed with the state: after the period, not in the future.
confirmation string or null The state's confirmation number. Optional. (max length 200)

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/sales-tax/returns" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "confirmation": "TX-0918-55821",
  "filed_on": "2026-09-18",
  "period": "2026-08",
  "state": "US-TX"
}'

Responses

Status Meaning Body
201 Created SalesTaxReturnOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "entity": "string",
  "currency": "string",
  "state": "string",
  "state_name": "string",
  "permit_number": "string",
  "period": "string",
  "start": "2026-09-21",
  "end": "2026-09-21",
  "due_on": "2026-09-21",
  "status": "awaiting_approval",
  "figures": {
    "gross_sales": "string",
    "exempt_customer_sales": "string",
    "exempt_product_sales": "string",
    "taxable_sales": "string",
    "tax": "string",
    "use_tax_purchases": "string",
    "use_tax": "string",
    "tax_due": "string",
    "jurisdictions": [
      {
        "code": null,
        "name": null,
        "level": null,
        "rate": null,
        "taxable": null,
        "tax": null,
        "purchases": null,
        "use_tax": null
      }
    ]
  },
  "account": "string",
  "ledger_tax": "string",
  "unexplained_difference": "string",
  "changed_since_filing": "string",
  "filing": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "filed_on": "2026-09-21",
    "confirmation": "string",
    "filed_by": {},
    "filed_at": "2026-09-21T09:30:00Z",
    "approved_by": {},
    "approved_at": "2026-09-21T09:30:00Z",
    "figures": {
      "gross_sales": null,
      "exempt_customer_sales": null,
      "exempt_product_sales": null,
      "taxable_sales": null,
      "tax": null,
      "use_tax_purchases": null,
      "use_tax": null,
      "tax_due": null,
      "jurisdictions": null
    },
    "tax_due": "string",
    "paid_amount": "string",
    "payment_difference": "string",
    "payments": [
      null
    ],
    "test_copy": true
  }
}

GET /v1/entities/{entity}/sales-tax/returns/{state}/{period}

Get a state's sales tax return

The state's return for the period from the posted journal lines: gross, exempt and taxable sales, the tax by jurisdiction, the check against the state's account, and the filing with its payments when recorded. Answers 422 for a period not of the state's filing frequency.

Required scope read (admin implies every scope)
Operation id get_return_v1_entities__entity__sales_tax_returns__state___period__get

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
path state string yes The state, e.g. US-CA. (pattern ^US-[A-Z]{2}$)
path period string yes 2026-08, 2026-Q3 or 2026, by the state's filing frequency. (pattern `^\d{4}(-(0[1-9]

Example request

curl "$ERP_URL/v1/entities/CH01/sales-tax/returns/{state}/{period}" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK SalesTaxReturnOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "entity": "string",
  "currency": "string",
  "state": "string",
  "state_name": "string",
  "permit_number": "string",
  "period": "string",
  "start": "2026-09-21",
  "end": "2026-09-21",
  "due_on": "2026-09-21",
  "status": "awaiting_approval",
  "figures": {
    "gross_sales": "string",
    "exempt_customer_sales": "string",
    "exempt_product_sales": "string",
    "taxable_sales": "string",
    "tax": "string",
    "use_tax_purchases": "string",
    "use_tax": "string",
    "tax_due": "string",
    "jurisdictions": [
      {
        "code": null,
        "name": null,
        "level": null,
        "rate": null,
        "taxable": null,
        "tax": null,
        "purchases": null,
        "use_tax": null
      }
    ]
  },
  "account": "string",
  "ledger_tax": "string",
  "unexplained_difference": "string",
  "changed_since_filing": "string",
  "filing": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "filed_on": "2026-09-21",
    "confirmation": "string",
    "filed_by": {},
    "filed_at": "2026-09-21T09:30:00Z",
    "approved_by": {},
    "approved_at": "2026-09-21T09:30:00Z",
    "figures": {
      "gross_sales": null,
      "exempt_customer_sales": null,
      "exempt_product_sales": null,
      "taxable_sales": null,
      "tax": null,
      "use_tax_purchases": null,
      "use_tax": null,
      "tax_due": null,
      "jurisdictions": null
    },
    "tax_due": "string",
    "paid_amount": "string",
    "payment_difference": "string",
    "payments": [
      null
    ],
    "test_copy": true
  }
}

GET /v1/products/{product_id}/sales-tax

Get a product's sales tax category

The product's category; general when none was set.

Required scope read (admin implies every scope)
Operation id get_product_v1_products__product_id__sales_tax_get

Parameters

In Name Type Required Description
path product_id string (uuid) yes Id of the product (GET /v1/products).

Example request

curl "$ERP_URL/v1/products/{product_id}/sales-tax" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK SalesTaxProductOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "product": "string",
  "category": "clothing"
}

PUT /v1/products/{product_id}/sales-tax

Set a product's sales tax category

The product's category, which decides with the state's rules how much of its price is taxed. Needs the admin scope and the policy action manage_accounts.

Required scope admin (admin implies every scope)
Idempotency-Key not used
Operation id put_product_v1_products__product_id__sales_tax_put

Parameters

In Name Type Required Description
path product_id string (uuid) yes Id of the product (GET /v1/products).

Request body

application/json (required): SalesTaxProductInput

Field Type Required Description
category "clothing" | "digital_goods" | "general" | "groceries" | "medical" | "professional_services" | "saas" yes The product's sales tax category.

Example request

curl -X PUT "$ERP_URL/v1/products/{product_id}/sales-tax" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "category": "saas"
}'

Responses

Status Meaning Body
200 OK SalesTaxProductOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "product": "string",
  "category": "clothing"
}

POST /v1/sales-tax-returns/{sales_tax_return_id}/approve

Approve a sales tax filing

A second person approves the filing recorded by someone else (403 for whoever recorded it, under the separation-of-duties rule finalize_own_vat_return); only an approved filing is paid. Answers 409 when already approved or withdrawn. A person's act (finalize_vat_return).

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id approve_v1_sales_tax_returns__sales_tax_return_id__approve_post

Parameters

In Name Type Required Description
path sales_tax_return_id string (uuid) yes Id of a filed sales tax return.

Example request

curl -X POST "$ERP_URL/v1/sales-tax-returns/{sales_tax_return_id}/approve" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

Responses

Status Meaning Body
200 OK SalesTaxReturnOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "entity": "string",
  "currency": "string",
  "state": "string",
  "state_name": "string",
  "permit_number": "string",
  "period": "string",
  "start": "2026-09-21",
  "end": "2026-09-21",
  "due_on": "2026-09-21",
  "status": "awaiting_approval",
  "figures": {
    "gross_sales": "string",
    "exempt_customer_sales": "string",
    "exempt_product_sales": "string",
    "taxable_sales": "string",
    "tax": "string",
    "use_tax_purchases": "string",
    "use_tax": "string",
    "tax_due": "string",
    "jurisdictions": [
      {
        "code": null,
        "name": null,
        "level": null,
        "rate": null,
        "taxable": null,
        "tax": null,
        "purchases": null,
        "use_tax": null
      }
    ]
  },
  "account": "string",
  "ledger_tax": "string",
  "unexplained_difference": "string",
  "changed_since_filing": "string",
  "filing": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "filed_on": "2026-09-21",
    "confirmation": "string",
    "filed_by": {},
    "filed_at": "2026-09-21T09:30:00Z",
    "approved_by": {},
    "approved_at": "2026-09-21T09:30:00Z",
    "figures": {
      "gross_sales": null,
      "exempt_customer_sales": null,
      "exempt_product_sales": null,
      "taxable_sales": null,
      "tax": null,
      "use_tax_purchases": null,
      "use_tax": null,
      "tax_due": null,
      "jurisdictions": null
    },
    "tax_due": "string",
    "paid_amount": "string",
    "payment_difference": "string",
    "payments": [
      null
    ],
    "test_copy": true
  }
}

GET /v1/sales-tax-returns/{sales_tax_return_id}/payment-candidates

List the entries that may have paid a sales tax return

Posted entries of the company dated after the period, with a debit on the state's account that is no sales tax posting, not linked to a return yet: typically the bank entry made by matching the state's debit on the bank worksheet.

Required scope read (admin implies every scope)
Operation id payment_candidates_v1_sales_tax_returns__sales_tax_return_id__payment_candidates_get

Parameters

In Name Type Required Description
path sales_tax_return_id string (uuid) yes Id of a filed sales tax return.

Example request

curl "$ERP_URL/v1/sales-tax-returns/{sales_tax_return_id}/payment-candidates" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK array of SalesTaxPaymentCandidate
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

[
  {
    "journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "entry_number": "string",
    "entry_date": "2026-09-21",
    "description": "string",
    "source": "string",
    "amount": "string"
  }
]

POST /v1/sales-tax-returns/{sales_tax_return_id}/payments

Link the entry that paid a sales tax return

Links a posted entry as paying the filed return: its amount is its debit on the state's account. The return is paid once the links cover tax_due. Answers 409 for a withdrawn filing or an entry already linked, 422 for an entry of another company, not posted, dated in the period or without a debit on the account. A person's act (finalize_vat_return).

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id link_payment_v1_sales_tax_returns__sales_tax_return_id__payments_post

Parameters

In Name Type Required Description
path sales_tax_return_id string (uuid) yes Id of a filed sales tax return.

Request body

application/json (required): SalesTaxPaymentInput

Field Type Required Description
journal_entry_id string (uuid) yes The posted entry that paid the state: of the return's company, dated after the period, with a debit on the state's account (GET .../payment-candidates).

Example request

curl -X POST "$ERP_URL/v1/sales-tax-returns/{sales_tax_return_id}/payments" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'

Responses

Status Meaning Body
201 Created SalesTaxReturnOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "entity": "string",
  "currency": "string",
  "state": "string",
  "state_name": "string",
  "permit_number": "string",
  "period": "string",
  "start": "2026-09-21",
  "end": "2026-09-21",
  "due_on": "2026-09-21",
  "status": "awaiting_approval",
  "figures": {
    "gross_sales": "string",
    "exempt_customer_sales": "string",
    "exempt_product_sales": "string",
    "taxable_sales": "string",
    "tax": "string",
    "use_tax_purchases": "string",
    "use_tax": "string",
    "tax_due": "string",
    "jurisdictions": [
      {
        "code": null,
        "name": null,
        "level": null,
        "rate": null,
        "taxable": null,
        "tax": null,
        "purchases": null,
        "use_tax": null
      }
    ]
  },
  "account": "string",
  "ledger_tax": "string",
  "unexplained_difference": "string",
  "changed_since_filing": "string",
  "filing": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "filed_on": "2026-09-21",
    "confirmation": "string",
    "filed_by": {},
    "filed_at": "2026-09-21T09:30:00Z",
    "approved_by": {},
    "approved_at": "2026-09-21T09:30:00Z",
    "figures": {
      "gross_sales": null,
      "exempt_customer_sales": null,
      "exempt_product_sales": null,
      "taxable_sales": null,
      "tax": null,
      "use_tax_purchases": null,
      "use_tax": null,
      "tax_due": null,
      "jurisdictions": null
    },
    "tax_due": "string",
    "paid_amount": "string",
    "payment_difference": "string",
    "payments": [
      null
    ],
    "test_copy": true
  }
}

POST /v1/sales-tax-returns/{sales_tax_return_id}/payments/{sales_tax_payment_id}/unlink

Unlink a payment from a sales tax return

Undoes a payment link made by mistake, with the reason; the entry stays posted. A person's act (finalize_vat_return).

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id unlink_payment_v1_sales_tax_returns__sales_tax_return_id__payments__sales_tax_payment_id__unlink_post

Parameters

In Name Type Required Description
path sales_tax_return_id string (uuid) yes Id of a filed sales tax return.
path sales_tax_payment_id string (uuid) yes Id of the payment link (payments[].id).

Request body

application/json (required): SalesTaxReasonInput

Field Type Required Description
reason string yes Why, in a few words; kept in the history. (min length 1; max length 500)

Example request

curl -X POST "$ERP_URL/v1/sales-tax-returns/{sales_tax_return_id}/payments/{sales_tax_payment_id}/unlink" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "Filed twice"
}'

Responses

Status Meaning Body
200 OK SalesTaxReturnOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "entity": "string",
  "currency": "string",
  "state": "string",
  "state_name": "string",
  "permit_number": "string",
  "period": "string",
  "start": "2026-09-21",
  "end": "2026-09-21",
  "due_on": "2026-09-21",
  "status": "awaiting_approval",
  "figures": {
    "gross_sales": "string",
    "exempt_customer_sales": "string",
    "exempt_product_sales": "string",
    "taxable_sales": "string",
    "tax": "string",
    "use_tax_purchases": "string",
    "use_tax": "string",
    "tax_due": "string",
    "jurisdictions": [
      {
        "code": null,
        "name": null,
        "level": null,
        "rate": null,
        "taxable": null,
        "tax": null,
        "purchases": null,
        "use_tax": null
      }
    ]
  },
  "account": "string",
  "ledger_tax": "string",
  "unexplained_difference": "string",
  "changed_since_filing": "string",
  "filing": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "filed_on": "2026-09-21",
    "confirmation": "string",
    "filed_by": {},
    "filed_at": "2026-09-21T09:30:00Z",
    "approved_by": {},
    "approved_at": "2026-09-21T09:30:00Z",
    "figures": {
      "gross_sales": null,
      "exempt_customer_sales": null,
      "exempt_product_sales": null,
      "taxable_sales": null,
      "tax": null,
      "use_tax_purchases": null,
      "use_tax": null,
      "tax_due": null,
      "jurisdictions": null
    },
    "tax_due": "string",
    "paid_amount": "string",
    "payment_difference": "string",
    "payments": [
      null
    ],
    "test_copy": true
  }
}

POST /v1/sales-tax-returns/{sales_tax_return_id}/withdraw

Withdraw a sales tax filing

Withdraws a filing recorded by mistake, with the reason (kept; the record is not deleted). Answers 409 while payments are linked. A person's act (finalize_vat_return).

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id withdraw_v1_sales_tax_returns__sales_tax_return_id__withdraw_post

Parameters

In Name Type Required Description
path sales_tax_return_id string (uuid) yes Id of a filed sales tax return.

Request body

application/json (required): SalesTaxReasonInput

Field Type Required Description
reason string yes Why, in a few words; kept in the history. (min length 1; max length 500)

Example request

curl -X POST "$ERP_URL/v1/sales-tax-returns/{sales_tax_return_id}/withdraw" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "Filed twice"
}'

Responses

Status Meaning Body
200 OK SalesTaxReturnOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "entity": "string",
  "currency": "string",
  "state": "string",
  "state_name": "string",
  "permit_number": "string",
  "period": "string",
  "start": "2026-09-21",
  "end": "2026-09-21",
  "due_on": "2026-09-21",
  "status": "awaiting_approval",
  "figures": {
    "gross_sales": "string",
    "exempt_customer_sales": "string",
    "exempt_product_sales": "string",
    "taxable_sales": "string",
    "tax": "string",
    "use_tax_purchases": "string",
    "use_tax": "string",
    "tax_due": "string",
    "jurisdictions": [
      {
        "code": null,
        "name": null,
        "level": null,
        "rate": null,
        "taxable": null,
        "tax": null,
        "purchases": null,
        "use_tax": null
      }
    ]
  },
  "account": "string",
  "ledger_tax": "string",
  "unexplained_difference": "string",
  "changed_since_filing": "string",
  "filing": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "filed_on": "2026-09-21",
    "confirmation": "string",
    "filed_by": {},
    "filed_at": "2026-09-21T09:30:00Z",
    "approved_by": {},
    "approved_at": "2026-09-21T09:30:00Z",
    "figures": {
      "gross_sales": null,
      "exempt_customer_sales": null,
      "exempt_product_sales": null,
      "taxable_sales": null,
      "tax": null,
      "use_tax_purchases": null,
      "use_tax": null,
      "tax_due": null,
      "jurisdictions": null
    },
    "tax_due": "string",
    "paid_amount": "string",
    "payment_difference": "string",
    "payments": [
      null
    ],
    "test_copy": true
  }
}

Schemas

SalesTaxAreaInput

Field Type Required Description
state string yes The state, e.g. US-CA. (pattern ^US-[A-Z]{2}$)
code string or null A code of its own, e.g. LOS-ANGELES. Omitted: made from the name (capitals, digits and hyphens, with -2 when taken). (pattern ^[A-Z0-9][A-Z0-9-]{0,39}$)
name string yes Its name. (min length 1; max length 120)
jurisdictions array of string yes The codes of its local jurisdictions (county, city, district) in the same state; the state's own rate always applies on top. (max items 20)

SalesTaxAreaOut

Field Type Required Description
id string (uuid) yes Id of the area.
state string yes The state.
code string yes Its code.
name string yes Its name.
jurisdictions array of string yes The codes of its local jurisdictions.
combined_rate string or null yes State plus local rates in force on on; null when one has no rate yet.

SalesTaxAreaUpdate

Field Type Required Description
name string or null New name. (min length 1; max length 120)
jurisdictions array of string or null The codes of its local jurisdictions. (max items 20)

SalesTaxCategoryRuleInput

Field Type Required Description
state string yes The state. (pattern ^US-[A-Z]{2}$)
category "clothing" | "digital_goods" | "general" | "groceries" | "medical" | "professional_services" | "saas" yes The product category.
taxable_percent decimal string yes How much of the price the state taxes: 0 exempt, 100 fully taxed, 80 for Texas data processing. (min 0; max 100)
note string or null Why, e.g. the statute. (max length 300)

SalesTaxCategoryRuleOut

Field Type Required Description
state string yes The state.
category "clothing" | "digital_goods" | "general" | "groceries" | "medical" | "professional_services" | "saas" yes The product category.
taxable_percent string yes How much of the price is taxed, in percent.
note string or null yes Why, or null.

SalesTaxCustomerInput

Field Type Required Description
area string or null The tax area of the customer's location (its code), or null.
exemption_number string or null The exemption certificate's number, or null. (max length 60)
exemption_reason "government" | "manufacturing" | "nonprofit" | "other" | "resale" or null Why it is exempt: resale, nonprofit, government, manufacturing or other; required with a number.
exempt_from string (date) or null The first day the certificate is valid; with a number, the day it is recorded when omitted.
exempt_until string (date) or null The certificate's expiry; null when it does not expire.
exempt_states array of string or null The states the certificate is valid in (e.g. a multistate certificate); with a number, the customer's own state when omitted.

SalesTaxCustomerOut

Field Type Required Description
party string yes The customer's code.
state string or null yes The customer's state, e.g. US-CA, or null.
area string or null yes Its tax area's code, or null.
area_name string or null yes Its tax area's name, or null.
exemption_number string or null yes Its exemption certificate's number.
exemption_reason "government" | "manufacturing" | "nonprofit" | "other" | "resale" or null yes Why it is exempt.
exempt_from string (date) or null yes The first day the certificate is valid.
exempt_until string (date) or null yes When the certificate expires.
exempt_states array of string yes The states the certificate is valid in.
exempt_today boolean yes True when the certificate is valid today in the customer's state.

SalesTaxFigures

What a state's return asks for, from the posted journal lines of the period.

Field Type Required Description
gross_sales string yes All sales to customers in the state, net of credit notes and discounts.
exempt_customer_sales string yes Sales to customers with a valid exemption certificate.
exempt_product_sales string yes Sales of products, or parts of their price, the state does not tax.
taxable_sales string yes Gross sales less the exempt ones.
tax string yes The sales tax collected.
use_tax_purchases string yes Purchases the vendor charged no sales tax on, used in the state: the use tax base.
use_tax string yes The use tax self-assessed on them.
tax_due string yes tax plus use_tax: what is due to the state.
jurisdictions array of SalesTaxJurisdictionLine yes The tax collected by jurisdiction and rate, state first.

SalesTaxFileInput

Field Type Required Description
state string yes The state, e.g. US-TX. (pattern ^US-[A-Z]{2}$)
period string yes The period by the registration's frequency: 2026-08, 2026-Q3, 2026. (pattern `^\d{4}(-(0[1-9]
filed_on string (date) yes The day it was filed with the state: after the period, not in the future.
confirmation string or null The state's confirmation number. Optional. (max length 200)

SalesTaxFilingOut

Field Type Required Description
id string (uuid) yes Id of the filed return.
filed_on string (date) yes The day it was filed with the state.
confirmation string or null yes The state's confirmation number, or null.
filed_by object yes Who recorded the filing (type and id).
filed_at string (date-time) yes When it was recorded (UTC).
approved_by object or null yes Who approved the filing (a second person), or null while awaiting approval.
approved_at string (date-time) or null yes When it was approved (UTC), or null.
figures SalesTaxFigures yes The figures as filed.
tax_due string yes The tax filed: what the payments must cover.
paid_amount string yes What the linked payments cover.
payment_difference string yes tax_due minus paid_amount: what is still to pay; negative when more.
payments array of SalesTaxPaymentOut yes The linked payments, oldest first.
test_copy boolean yes True in a test copy: a record only, nothing is filed.

SalesTaxJurisdictionInput

Field Type Required Description
state string yes The state it lies in, e.g. US-CA. (pattern ^US-[A-Z]{2}$)
code string or null A code of its own, e.g. CA-LA-DIST. Omitted: made from the name (capitals, digits and hyphens, with -2 when taken). (pattern ^[A-Z0-9][A-Z0-9-]{0,39}$)
name string yes Its name. (min length 1; max length 120)
level "city" | "county" | "district" yes county, city or district.
rate decimal string yes Its rate in percent. (min 0)
valid_from string (date) yes The first day of the rate.

SalesTaxJurisdictionLine

Field Type Required Description
code string yes The jurisdiction's code.
name string yes Its name.
level string yes state, county, city or district.
rate string yes The rate charged, in percent.
taxable string yes The taxable sales it was charged on.
tax string yes The tax collected for it.
purchases string yes The purchases use tax was self-assessed on for it.
use_tax string yes The use tax self-assessed for it.

SalesTaxJurisdictionOut

Field Type Required Description
id string (uuid) yes Id of the jurisdiction.
state string yes The state it lies in.
code string yes Its code.
name string yes Its name.
level "city" | "county" | "district" | "state" yes state, county, city or district.
rate string or null yes The rate in force on on, or null if none yet.
rates array of SalesTaxRateOut yes Every rate, oldest first.

SalesTaxPaymentCandidate

Field Type Required Description
journal_entry_id string (uuid) yes Id of the posted entry.
entry_number string or null yes Its number.
entry_date string (date) yes Its date.
description string yes Its description.
source string yes Its source, such as bank or manual.
amount string yes What it debits to the state's account.

SalesTaxPaymentInput

Field Type Required Description
journal_entry_id string (uuid) yes The posted entry that paid the state: of the return's company, dated after the period, with a debit on the state's account (GET .../payment-candidates).

SalesTaxPaymentOut

Field Type Required Description
id string (uuid) yes Id of the link (the {payment_id} to unlink it).
journal_entry_id string (uuid) yes Id of the posted entry that paid.
entry_number string or null yes Number of that entry.
paid_on string (date) yes The entry's date (ISO 8601 date).
amount string yes What it debited to the state's account.
linked_by object yes Who linked it (type and id).
linked_at string (date-time) yes When it was linked (ISO 8601 timestamp, UTC).

SalesTaxPeriodRow

Field Type Required Description
state string yes The state.
state_name string yes Its name.
period string yes 2026-08, 2026-Q3 or 2026.
start string (date) yes First day.
end string (date) yes Last day.
due_on string (date) yes The day the return is due with the state.
status "awaiting_approval" | "due_soon" | "filed" | "open" | "overdue" | "paid" | "to_file" yes open (the period has not ended), to_file, due_soon (due within 10 days), overdue (not recorded as filed by its due day), awaiting_approval (recorded as filed, not approved by a second person yet), filed or paid.
tax string yes The tax due for the period (sales and use tax), from the ledger now.
tax_due string or null yes The tax filed, or null when not filed.
filed_on string (date) or null yes The day it was filed, or null.
return_id string (uuid) or null yes Id of the filed return, or null.

SalesTaxPeriodsOut

Field Type Required Description
entity string yes The company's code.
year integer yes The calendar year.
currency string yes The currency of the amounts.
periods array of SalesTaxPeriodRow yes By state, then period.
unregistered array of UnregisteredStateRow yes Sales of the year into states the company is not registered in: what economic nexus is measured on.

SalesTaxProductInput

Field Type Required Description
category "clothing" | "digital_goods" | "general" | "groceries" | "medical" | "professional_services" | "saas" yes The product's sales tax category.

SalesTaxProductOut

Field Type Required Description
product string yes The product's code.
category "clothing" | "digital_goods" | "general" | "groceries" | "medical" | "professional_services" | "saas" yes Its sales tax category (general when not set).

SalesTaxRateInput

Field Type Required Description
valid_from string (date) yes The first day of the rate; a rate of the same day is replaced.
rate decimal string yes The rate in percent. (min 0)
source string or null Where it comes from, e.g. the state's notice. (max length 500)

SalesTaxRateOut

Field Type Required Description
valid_from string (date) yes The first day of the rate (ISO 8601 date).
rate string yes Percent, e.g. 7.25.
source string or null yes Where the rate came from, e.g. data-vNext's series.

SalesTaxReasonInput

Field Type Required Description
reason string yes Why, in a few words; kept in the history. (min length 1; max length 500)

SalesTaxRegistrationInput

Field Type Required Description
state string yes The state, ISO 3166-2, e.g. US-CA. (pattern ^US-[A-Z]{2}$)
permit_number string or null The state's seller's permit or account number. (max length 60)
registered_from string (date) yes The first day tax is charged in the state.
registered_until string (date) or null The last day, when the registration ended; null while in force.
filing_frequency "month" | "quarter" | "year" yes How often the state's return is filed: month, quarter or year.
state_rate decimal string or null The state rate in percent, from registered_from. Omit to take the state base rate data-vNext has (422 sales_tax_state_rate_unknown when it has none). Ignored when the state already has its jurisdiction. (min 0)
use_tax_area string or null In the company's own state: the tax area (its code) where it uses what it buys, whose local rates its use tax is self-assessed at; null for the state rate only.

SalesTaxRegistrationOut

Field Type Required Description
id string (uuid) yes Id of the registration.
state string yes The state, e.g. US-CA.
state_name string yes The state's name, e.g. California.
permit_number string or null yes The state's permit number, or null.
registered_from string (date) yes First day tax is charged (ISO 8601 date).
registered_until string (date) or null yes Last day, or null while in force.
filing_frequency "month" | "quarter" | "year" yes month, quarter or year.
account string yes The liability account the state's tax is booked to.
account_name string yes That account's name.
use_tax_area string or null yes The tax area use tax is self-assessed at.
balance string yes That account's balance today, credit positive: the tax owed to the state.

SalesTaxRegistrationUpdate

Only the fields sent change; send registered_until: null to reopen a registration.

Field Type Required Description
permit_number string or null The state's permit number. (max length 60)
registered_until string (date) or null The last day of the registration; null while in force.
filing_frequency "month" | "quarter" | "year" or null How often it files; not changed once a return is recorded as filed (409 sales_tax_frequency_filed).
use_tax_area string or null The use tax area's code; null for the state rate only.

SalesTaxReturnOut

Field Type Required Description
entity string yes The company's code.
currency string yes The currency of the amounts (the company's).
state string yes The state, e.g. US-CA.
state_name string yes The state's name.
permit_number string or null yes The company's permit number in the state.
period string yes 2026-08, 2026-Q3 or 2026.
start string (date) yes First day of the period.
end string (date) yes Last day of the period.
due_on string (date) yes The day the return is due with the state.
status "awaiting_approval" | "due_soon" | "filed" | "open" | "overdue" | "paid" | "to_file" yes open (the period has not ended), to_file, due_soon (due within 10 days), overdue (not recorded as filed by its due day), awaiting_approval (recorded as filed, not approved by a second person yet), filed or paid.
figures SalesTaxFigures yes The figures from the ledger as it is now.
account string yes The state's liability account.
ledger_tax string yes The movement on that account in the period, credit positive, without the entries linked as paying a return.
unexplained_difference string yes ledger_tax minus figures.tax_due: entries on the account that are not sales tax postings (a manual journal, a payment not linked yet); zero when it ties.
changed_since_filing string or null yes The tax due now less the tax filed: postings dated in the period after it was filed (file an amended return with the state); null when not filed.
filing SalesTaxFilingOut or null yes The filing in force, or null.

SalesTaxSetupOut

Field Type Required Description
entity string yes The company's code.
applies boolean yes True for a company in the US: its sales use sales tax.
on string (date) yes The day the rates in force are shown for.
categories array of "clothing" | "digital_goods" | "general" | "groceries" | "medical" | "professional_services" | "saas" yes The product categories, in order.
registrations array of SalesTaxRegistrationOut yes By state.
jurisdictions array of SalesTaxJurisdictionOut yes By state, then code.
areas array of SalesTaxAreaOut yes By state, then code.
rules array of SalesTaxCategoryRuleOut yes Product taxability by state; a category without a rule is taxed in full.

UnregisteredStateRow

Field Type Required Description
state string yes A state the company sold into without a registration.
state_name string yes Its name.
sales string yes Its sales there in the period, net of credit notes.
documents integer yes How many invoices and credit notes.
Stabiso, the Enterprise System of Record · generated from the code and its guides · release 6f03a5e846b3