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
APItab.
Errors
The API does not return a structured error object.
Common responses:
| Status | Situation | Example body |
|---|---|---|
400 Bad Request | Missing or empty description (customs tariff proposals, classification proposals) | description must not be null in JSON |
400 Bad Request | Missing or empty direction (customs tariff proposals) | direction must not be null in JSON |
400 Bad Request | Missing proposal id in the path, or malformed JSON body | (message or empty body) |
403 Forbidden | Missing/invalid token, or insufficient role | Unauthorized |
403 Forbidden | Tenant proposal quota exhausted | Customs tariff proposals limit reached / Classification proposals limit reached |
404 Not Found | Proposal id does not exist in the tenant | (empty body) |
429 Too Many Requests | Only on auth/OAuth endpoints, not on the proposal endpoints | Too 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.
Navigate to Customs Tariff Proposal New Screen
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.
| Field | Type | Description |
|---|---|---|
id | UUID string | Unique id of the proposal. |
state | string | Workflow state, see Proposal State and Data Quality. |
data_quality | string | Confidence of the result: low, medium or high. See Proposal State and Data Quality. |
code | string | The determined customs tariff code (8 digits for export, 11 digits for import). |
direction | string | export (8-digit code) or import (11-digit code). |
description | string | The product description that was submitted. |
tariff_system | string | Tariff system, e.g. de or ch. |
bti_code | string | Customs code of a matched Binding Tariff Information (BTI), if any. |
bti_reference | string | Reference number of the matched BTI, if any. |
proposed_customs_description | string | The AI-proposed customs goods description in German (same value as proposed_customs_description_de). |
proposed_customs_description_de | string | The AI-proposed customs goods description in German. |
proposed_customs_description_en | string | The AI-proposed customs goods description in English. |
proposed_customs_description_short | string | A short form of the proposed customs goods description in German (same value as proposed_customs_description_short_de). |
proposed_customs_description_short_de | string | A short form of the proposed customs goods description in German. |
proposed_customs_description_short_en | string | A short form of the proposed customs goods description in English. |
reference_id | string | Surrogate key you supplied to identify the proposal in your system. |
vendor_id | string | Vendor id you supplied to associate the proposal with a vendor in your system. |
job_id | UUID string | Id of the batch job the proposal belongs to, if any. |
created_at | number | Creation time as a Unix timestamp in milliseconds. |
updated_at | number | Last update time as a Unix timestamp in milliseconds. |
memo_markdown_de | string | The full generated memo in German (Markdown). Only in single-proposal GET/POST responses, not in list responses. |
memo_markdown_en | string | The full generated memo in English (Markdown). Only in single-proposal GET/POST responses, not in list responses. |
authorize_url | string | URL 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:
state | Meaning |
|---|---|
start | Proposal was created and is queued for processing. |
product_done | The product analysis step has finished. |
code_done | The customs tariff code has been determined. |
bti_done | The Binding Tariff Information (BTI) matching step has finished. |
ruling_done | The customs ruling step has finished. |
training_done | The training data matching step has finished. |
end | Processing is complete. This is the terminal state. |
Notes:
- The
stateis a plain string. Before the first step runs it may briefly bestart; 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
stateisend. PollGETuntil 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-
endstate 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_quality | Meaning |
|---|---|
high | A code was found and all relevant information was available. |
medium | A code was found, but some information is missing — the result may be improvable. |
low | No 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.
Navigate to Classification Proposal New Screen
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.
| Field | Type | Description |
|---|---|---|
id | UUID string | Unique id of the proposal. |
state | string | Workflow state, see Proposal State. |
description | string | The product description that was submitted. |
identification_military_equipment | string | Result of the armaments (military equipment) check. Only in single-proposal GET/POST responses. |
identification_dual_use_eu | string | Result of the EU dual-use check. Only in single-proposal GET/POST responses. |
identification_dual_use_de | string | Result of the German dual-use check. Only in single-proposal GET/POST responses. |
reference_id | string | Surrogate key you supplied to identify the proposal in your system. |
vendor_id | string | Vendor id you supplied to associate the proposal with a vendor in your system. |
created_at | number | Creation time as a Unix timestamp in milliseconds. |
updated_at | number | Last update time as a Unix timestamp in milliseconds. |
authorize_url | string | URL 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:
state | Meaning |
|---|---|
start | Proposal was created and is queued for processing. |
product_done | The product analysis step has finished. |
military_done | The armaments (military equipment) check has finished. |
dual_use_eu_done | The EU dual-use check has finished. |
dual_use_de_done | The German dual-use check has finished. |
end | Processing is complete. This is the terminal state. |
Notes:
- The
stateis a plain string. Before the first step runs it may briefly bestart; 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-
endstate 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.
| Field | Type | Description |
|---|---|---|
id | UUID string | Unique id of the tenant. |
name | string | Display name of the tenant. |
classification_proposals_count | number | Number of classification proposals created in the current billing period. |
classification_proposals_limit | number | Contractual limit of classification proposals for the tenant. |
customs_tariff_proposals_count | number | Number of customs tariff proposals created in the current billing period. |
customs_tariff_proposals_limit | number | Contractual 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
}