Skip to main content

REST API

TariffPilot offers a REST API for programmatically accessing customs tariff proposals.

Authorization

All of the following endpoints require authorization with a bearer access token. Such an access token can be retrieved at cloud.tariffpilot.com in the tenant settings at tab API. Please note that the access token is associated to the user account and tenant it has been generated for. Thus removing an user from a tenant invalidates the access token.

Token Binding and Validity

  • Bound to user and tenant: An access token is associated to the user account and tenant it has been generated for. It cannot be used across tenants. Removing the user from the tenant invalidates the access token immediately.
  • Expiry: Access tokens are long-lived.
  • Secrecy: Treat the token like a password. To rotate a token, generate a new one in the API tab.

Errors

The API does not return a structured error object.

Common responses:

StatusSituationExample body
400 Bad RequestMissing or empty description (customs tariff proposals, classification proposals)description must not be null in JSON
400 Bad RequestMissing or empty direction (customs tariff proposals)direction must not be null in JSON
400 Bad RequestMissing proposal id in the path, or malformed JSON body(message or empty body)
403 ForbiddenMissing/invalid token, or insufficient roleUnauthorized
403 ForbiddenTenant proposal quota exhaustedCustoms tariff proposals limit reached / Classification proposals limit reached
404 Not FoundProposal id does not exist in the tenant(empty body)
429 Too Many RequestsOnly on auth/OAuth endpoints, not on the proposal endpointsToo Many Requests

Customs Tariff Proposals API

List All Customs Tariff Proposals as JSON

Returns a JSON list of all customs tariff proposals in the tenant.

curl -X GET "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json"

A list of all customs tariff proposals by vendor ID can be fetched with:

curl -X GET "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals/by_vendor_id?vendor_id=123" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json"

Get a Customs Tariff Proposal as JSON

Returns a JSON representation of the customs tariff proposal in the tenant.

curl -X GET "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals/<CUSTOMS_TARIFF_PROPOSAL_ID>" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json"

Get a Customs Tariff Proposal as PDF

Returns a PDF representation of the customs tariff proposal in the tenant.

curl -X GET "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals/pdf/<CUSTOMS_TARIFF_PROPOSAL_ID>" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/pdf"

Optionally, you can specify the language. The default language is en. Additional languages are de for German.

curl -X GET "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals/pdf/<CUSTOMS_TARIFF_PROPOSAL_ID>?language=de" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/pdf"

Get a Customs Tariff Proposal as Markdown

Returns a Markdown representation of the customs tariff proposal in the tenant.

curl -X GET "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals/markdown/<CUSTOMS_TARIFF_PROPOSAL_ID>" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: text/markdown"

Optionally, you can specify the language. The default language is en. Additional languages are de for German.

curl -X GET "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals/markdown/<CUSTOMS_TARIFF_PROPOSAL_ID>?language=de" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: text/markdown"

Get a Customs Tariff Proposal as HTML

Returns a HTML representation of the customs tariff proposal in the tenant.

curl -X GET "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals/html/<CUSTOMS_TARIFF_PROPOSAL_ID>" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: text/html"

Optionally, you can specify the language. The default language is en. Additional languages are de for German.

curl -X GET "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals/html/<CUSTOMS_TARIFF_PROPOSAL_ID>?language=de" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: text/html"

Delete a Customs Tariff Proposal

Deletes the customs tariff proposal in the tenant.

curl -X DELETE "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals/<CUSTOMS_TARIFF_PROPOSAL_ID>" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json"

Post a Customs Tariff Proposal

Posts a JSON representation of a customs tariff proposal into the tenant. The proposal will be created in the database and processed asynchronously.

curl -X POST "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json" \
-d '{ "description": "Nici Einhorn 80cm", "direction": "export", "search_web": true, "vendor_id": "123" }'

The property direction is mandatory and can be either export for 8-digit or import for 11-digit customs code. The property search_web is optional and defaults to false. When set to true, the web will be searched for the product descripton. The property vendor_id is optional and can be used to associate the customs tariff proposal with a vendor ID from your system.

curl -X POST "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json" \
-d '{ "description": "Nici Einhorn 80cm", "direction": "export", "job_id": <JOB_ID>, "search_web": true }'

The property job_id is optional and can be used to associate the customs tariff proposal with a job. The job_id must be a UUID4 string. If the job does not exist, it will be created.

Posts a request to open a customs tariff proposal New screen in the tenant. The proposal will not be created in the database. Instead a JSON objects with an authorize_url will be returned. The authorize_url can be used to open the customs tariff proposal in the TariffPilot web app.

curl -X POST "https://app-cloud.tariffpilot.com/api/customs_tariff_proposals/new" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json" \
-d '{ "description": "Nici Einhorn 80cm", "vendor_id": "123" }'

A service user will be created upon first request to open a customs tariff proposal. Currently there is no option to specify a delegate user that should appear as the creator of the customs tariff proposal in the TariffPilot web app. The property vendor_id is optional and can be used to associate the customs tariff proposal with a vendor ID from your system.

Response Fields

The customs_tariff_proposals endpoints return objects with the following fields. Fields that are null are omitted from the JSON response.

FieldTypeDescription
idUUID stringUnique id of the proposal.
statestringWorkflow state, see Proposal State and Data Quality.
data_qualitystringConfidence of the result: low, medium or high. See Proposal State and Data Quality.
codestringThe determined customs tariff code (8 digits for export, 11 digits for import).
directionstringexport (8-digit code) or import (11-digit code).
descriptionstringThe product description that was submitted.
tariff_systemstringTariff system, e.g. de or ch.
bti_codestringCustoms code of a matched Binding Tariff Information (BTI), if any.
bti_referencestringReference number of the matched BTI, if any.
proposed_customs_descriptionstringThe AI-proposed customs goods description in German (same value as proposed_customs_description_de).
proposed_customs_description_destringThe AI-proposed customs goods description in German.
proposed_customs_description_enstringThe AI-proposed customs goods description in English.
proposed_customs_description_shortstringA short form of the proposed customs goods description in German (same value as proposed_customs_description_short_de).
proposed_customs_description_short_destringA short form of the proposed customs goods description in German.
proposed_customs_description_short_enstringA short form of the proposed customs goods description in English.
reference_idstringSurrogate key you supplied to identify the proposal in your system.
vendor_idstringVendor id you supplied to associate the proposal with a vendor in your system.
job_idUUID stringId of the batch job the proposal belongs to, if any.
created_atnumberCreation time as a Unix timestamp in milliseconds.
updated_atnumberLast update time as a Unix timestamp in milliseconds.
memo_markdown_destringThe full generated memo in German (Markdown). Only in single-proposal GET/POST responses, not in list responses.
memo_markdown_enstringThe full generated memo in English (Markdown). Only in single-proposal GET/POST responses, not in list responses.
authorize_urlstringURL to open the proposal in the TariffPilot web app.

Note on timestamps: created_at and updated_at are serialized as numbers (epoch milliseconds), not as ISO-8601 strings.

Example JSON Responses

Immediately after POST /api/customs_tariff_proposals, the newly created proposal is returned. Processing is asynchronous, so state is start and result fields such as code are not yet present:

{
"id": "3f9a2c14-8b7e-4c2a-9f1d-2e5b6a7c8d90",
"state": "start",
"description": "Nici Einhorn 80cm",
"direction": "export",
"tariff_system": "de",
"reference_id": "PO-2026-0815",
"vendor_id": "123",
"created_at": 1754737200000,
"updated_at": 1754737200000,
"authorize_url": "https://app-cloud.tariffpilot.com/api/oauth2/callback?code=...&state=..."
}

After processing has finished, GET /api/customs_tariff_proposals/<CUSTOMS_TARIFF_PROPOSAL_ID> returns the completed proposal:

{
"id": "3f9a2c14-8b7e-4c2a-9f1d-2e5b6a7c8d90",
"state": "end",
"data_quality": "high",
"code": "95034910",
"description": "Nici Einhorn 80cm",
"direction": "export",
"tariff_system": "de",
"proposed_customs_description": "Stofftiere in Gestalt von Tieren",
"proposed_customs_description_de": "Stofftiere in Gestalt von Tieren",
"proposed_customs_description_en": "Stuffed animals in the form of animals",
"proposed_customs_description_short": "Stofftiere",
"proposed_customs_description_short_de": "Stofftiere",
"proposed_customs_description_short_en": "Stuffed animals",
"reference_id": "PO-2026-0815",
"vendor_id": "123",
"created_at": 1754737200000,
"updated_at": 1754737512000,
"memo_markdown_de": "# Zolltarifvorschlag\n\n...",
"memo_markdown_en": "# Customs Tariff Proposal\n\n...",
"authorize_url": "https://app-cloud.tariffpilot.com/api/oauth2/callback?code=...&state=..."
}

The list endpoint GET /api/customs_tariff_proposals returns a JSON array of the same objects, but without direction, bti_reference and the memo_markdown_de/memo_markdown_en fields:

[
{
"id": "3f9a2c14-8b7e-4c2a-9f1d-2e5b6a7c8d90",
"state": "end",
"data_quality": "high",
"code": "95034910",
"description": "Nici Einhorn 80cm",
"tariff_system": "de",
"reference_id": "PO-2026-0815",
"vendor_id": "123",
"created_at": 1754737200000,
"updated_at": 1754737512000,
"authorize_url": "https://app-cloud.tariffpilot.com/api/oauth2/callback?code=...&state=..."
}
]

The POST /api/customs_tariff_proposals/new endpoint does not create a proposal; it returns only an authorize_url:

{
"authorize_url": "https://app-cloud.tariffpilot.com/api/oauth2/callback?code=...&state=..."
}

Proposal State and Data Quality

The state field reflects the asynchronous workflow. For customs tariff proposals it takes one of the following values, in order:

stateMeaning
startProposal was created and is queued for processing.
product_doneThe product analysis step has finished.
code_doneThe customs tariff code has been determined.
bti_doneThe Binding Tariff Information (BTI) matching step has finished.
ruling_doneThe customs ruling step has finished.
training_doneThe training data matching step has finished.
endProcessing is complete. This is the terminal state.

Notes:

  • The state is a plain string. Before the first step runs it may briefly be start; in rare cases the field can be absent (null) for an object that has not been processed at all.
  • These states may be extended or changed in the future. Do not rely on the exact values, only on the order and the terminal state end.
  • A proposal is finished exactly when state is end. Poll GET until then.
  • The API does not expose a dedicated "waiting for clarification" state, and the AI's follow-up questions themselves are not part of the JSON returned by these endpoints. A proposal that stays in a non-end state without progressing over an extended period is a candidate for manual review.

The data_quality field indicates how confident the result is and whether information is missing:

data_qualityMeaning
highA code was found and all relevant information was available.
mediumA code was found, but some information is missing — the result may be improvable.
lowNo code could be found / information is insufficient.

Classification Proposals API

List All Classification Proposals as JSON

Returns a JSON list of all classification proposals in the tenant.

curl -X GET "https://app-cloud.tariffpilot.com/api/classification_proposals" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json"

A list of all classification proposals by vendor ID can be fetched with:

curl -X GET "https://app-cloud.tariffpilot.com/api/classification_proposals/by_vendor_id?vendor_id=123" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json"

Get a Classification Proposal as JSON

Returns a JSON representation of the classification proposal in the tenant.

curl -X GET "https://app-cloud.tariffpilot.com/api/classification_proposals/<CLASSIFICATION_PROPOSAL_ID>" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json"

Get a Classification Proposal as PDF

Returns a PDF representation of the classification proposal in the tenant.

curl -X GET "https://app-cloud.tariffpilot.com/api/classification_proposals/pdf/<CLASSIFICATION_PROPOSAL_ID>" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/pdf"

Optionally, you can specify the language. The default language is en. Additional languages are de for German.

curl -X GET "https://app-cloud.tariffpilot.com/api/classification_proposals/pdf/<CLASSIFICATION_PROPOSAL_ID>?language=de" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/pdf"

Get a Classification Proposal as Markdown

Returns a Markdown representation of the classification proposal in the tenant.

curl -X GET "https://app-cloud.tariffpilot.com/api/classification_proposals/markdown/<CLASSIFICATION_PROPOSAL_ID>" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: text/markdown"

Optionally, you can specify the language. The default language is en. Additional languages are de for German.

curl -X GET "https://app-cloud.tariffpilot.com/api/classification_proposals/markdown/<CLASSIFICATION_PROPOSAL_ID>?language=de" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: text/markdown"

Get a Classification Proposal as HTML

Returns a HTML representation of the classification proposal in the tenant.

curl -X GET "https://app-cloud.tariffpilot.com/api/classification_proposals/html/<CLASSIFICATION_PROPOSAL_ID>" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: text/html"

Optionally, you can specify the language. The default language is en. Additional languages are de for German.

curl -X GET "https://app-cloud.tariffpilot.com/api/classification_proposals/html/<CLASSIFICATION_PROPOSAL_ID>?language=de" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: text/html"

Delete a Classification Proposal

Deletes the classification proposal in the tenant.

curl -X DELETE "https://app-cloud.tariffpilot.com/api/classification_proposals/<CLASSIFICATION_PROPOSAL_ID>" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json"

Post a Classification Proposal

Posts a JSON representation of a classification proposal into the tenant. The proposal will be created in the database and processed asynchronously.

curl -X POST "https://app-cloud.tariffpilot.com/api/classification_proposals" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json" \
-d '{ "description": "Nici Einhorn 80cm", "search_web": true, "vendor_id": "123" }'

The property search_web is optional and defaults to false. When set to true, the web will be searched for the product descripton. The property vendor_id is optional and can be used to associate the classification proposal with a vendor ID from your system.

Posts a request to open a classification proposal New screen in the tenant. The proposal will not be created in the database. Instead a JSON objects with an authorize_url will be returned. The authorize_url can be used to open the classification proposal in the TariffPilot web app.

curl -X POST "https://app-cloud.tariffpilot.com/api/classification_proposals/new" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json" \
-d '{ "description": "Nici Einhorn 80cm", "vendor_id": "123" }'

A service user will be created upon first request to open a classification proposal. Currently there is no option to specify a delegate user that should appear as the creator of the classification proposal in the TariffPilot web app. The property vendor_id is optional and can be used to associate the classification proposal with a vendor ID from your system.

Response Fields

The classification_proposals endpoints return objects with the following fields. Fields that are null are omitted from the JSON response. Note that classification proposals do not carry a data_quality field.

FieldTypeDescription
idUUID stringUnique id of the proposal.
statestringWorkflow state, see Proposal State.
descriptionstringThe product description that was submitted.
identification_military_equipmentstringResult of the armaments (military equipment) check. Only in single-proposal GET/POST responses.
identification_dual_use_eustringResult of the EU dual-use check. Only in single-proposal GET/POST responses.
identification_dual_use_destringResult of the German dual-use check. Only in single-proposal GET/POST responses.
reference_idstringSurrogate key you supplied to identify the proposal in your system.
vendor_idstringVendor id you supplied to associate the proposal with a vendor in your system.
created_atnumberCreation time as a Unix timestamp in milliseconds.
updated_atnumberLast update time as a Unix timestamp in milliseconds.
authorize_urlstringURL to open the proposal in the TariffPilot web app.

Note on timestamps: created_at and updated_at are serialized as numbers (epoch milliseconds), not as ISO-8601 strings.

Example JSON Responses

Immediately after POST /api/classification_proposals, the newly created proposal is returned with state start:

{
"id": "a71c0e42-3d55-4f18-9b2c-1a6d4e7f8091",
"state": "start",
"description": "Nici Einhorn 80cm",
"reference_id": "PO-2026-0815",
"vendor_id": "123",
"created_at": 1754737200000,
"updated_at": 1754737200000,
"authorize_url": "https://app-cloud.tariffpilot.com/api/oauth2/callback?code=...&state=..."
}

After processing has finished, GET /api/classification_proposals/<CLASSIFICATION_PROPOSAL_ID> returns the completed proposal, including the identification results:

{
"id": "a71c0e42-3d55-4f18-9b2c-1a6d4e7f8091",
"state": "end",
"description": "Nici Einhorn 80cm",
"identification_military_equipment": "...",
"identification_dual_use_eu": "...",
"identification_dual_use_de": "...",
"reference_id": "PO-2026-0815",
"vendor_id": "123",
"created_at": 1754737200000,
"updated_at": 1754737512000,
"authorize_url": "https://app-cloud.tariffpilot.com/api/oauth2/callback?code=...&state=..."
}

The list endpoint GET /api/classification_proposals returns a JSON array of the same objects, but without the three identification_* fields.

Proposal State

The state field reflects the asynchronous workflow. For classification proposals it takes one of the following values, in order:

stateMeaning
startProposal was created and is queued for processing.
product_doneThe product analysis step has finished.
military_doneThe armaments (military equipment) check has finished.
dual_use_eu_doneThe EU dual-use check has finished.
dual_use_de_doneThe German dual-use check has finished.
endProcessing is complete. This is the terminal state.

Notes:

  • The state is a plain string. Before the first step runs it may briefly be start; in rare cases the field can be absent (null) for an object that has not been processed at all.
  • These states may be extended or changed in the future. Do not rely on the exact values, only on the order and the terminal state end.
  • The API does not expose a dedicated "waiting for clarification" state, and the AI's follow-up questions themselves are not part of the JSON returned by these endpoints. A proposal that stays in a non-end state without progressing over an extended period is a candidate for manual review.

Tenant API

Get Tenant Information

Returns a JSON representation of the tenant.

curl -X GET "https://app-cloud.tariffpilot.com/api/tenant/" \
-H "Authorization: Bearer <BEARER_TOKEN>" \
-H "Content-Type: application/json"

Response Fields

Fields that are null are omitted from the JSON response. This endpoint is read-only; there is no API endpoint to update tenant settings.

FieldTypeDescription
idUUID stringUnique id of the tenant.
namestringDisplay name of the tenant.
classification_proposals_countnumberNumber of classification proposals created in the current billing period.
classification_proposals_limitnumberContractual limit of classification proposals for the tenant.
customs_tariff_proposals_countnumberNumber of customs tariff proposals created in the current billing period.
customs_tariff_proposals_limitnumberContractual limit of customs tariff proposals for the tenant.

Example JSON Response

{
"id": "6b2f1a9e-4d3c-4a7b-8f21-0c9d5e7a1b34",
"name": "Acme GmbH",
"classification_proposals_count": 12,
"classification_proposals_limit": 100,
"customs_tariff_proposals_count": 348,
"customs_tariff_proposals_limit": 1000
}