Build
REST API
Every endpoint lives under https://api.tarifflo.com/api/v1, takes JSON and authenticates with X-API-Key. Long-running work answers 202 with a jobId and delivers the result to your webhook.
Classifications
Classify one product to an HTS code for each destination. The US code is computed first, then mapped to the other destinations.
/api/v1/classificationsscope: classificationsBody
| Field | Type | Description |
|---|---|---|
descriptionrequired | string | Product description, 10 to 2000 characters. |
destinationsrequired | string[] | 1 to 10 destination countries, 2-letter ISO codes. |
nameoptional | string | Product name, 3 to 200 characters. |
partialHtsCodeoptional | string | A known 4- or 6-digit prefix that constrains the result. |
webhookUrloptional | string | Public HTTPS URL that receives the signed result. Omit it to poll the GET endpoint instead. |
curl -X POST "https://api.tarifflo.com/api/v1/classifications" \
-H "X-API-Key: $TARIFFLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "Wireless over-ear headphones, plastic housing",
"destinations": ["US", "CA"],
"webhookUrl": "https://your-server.com/webhooks/tarifflo"
}'{
"jobId": "0193c1f2-...",
"status": "queued",
"destinationsQueued": 2,
"totalClassifications": 2,
"estimatedCompletionTime": 90,
"message": "Classification job queued. ..."
}/api/v1/classifications/{jobId}scope: classificationsReturns status queued, processing, complete (with data) or needs_info (with questions). To answer questions, add the answers to the description and classify again.
Bulk classification
Classify many short descriptions in one batch. Send up to 10,000 items inline, or upload a JSONL file of { "id", "text" } rows. The webhook carries a compact completion event; page through the results with GET.
/api/v1/classifications/batchscope: classificationsBody
Send exactly one of items or inputKey.
| Field | Type | Description |
|---|---|---|
webhookUrlrequired | string | HTTPS URL that receives classification_batch.completed. |
itemsoptional | { id?, text }[] | Inline items, at most 10,000. |
inputKeyoptional | string | The key of a JSONL file uploaded through /upload-url. |
/api/v1/classifications/batch/upload-urlscope: classificationsTakes filename and returns uploadUrl and inputKey. PUT the file (up to 100 MB) to uploadUrl within 15 minutes, then POST the batch with the inputKey.
/api/v1/classifications/batch/{batchId}?page=0scope: classificationsStatus, then results 1,000 per page once the batch is complete. Each result carries id, hts10, confidence and top5.
Tariff rates
Rate up to 50 lines. Each line returns the effective ad valorem rate with Chapter 99 stacking (Section 301, 232, IEEPA) and a cost breakdown, priced as of its date. Unknown field names are rejected, so a typo fails instead of returning a wrong rate.
/api/v1/tariff-ratesscope: tariff_ratesBody
| Field | Type | Description |
|---|---|---|
ratesrequired | object[] | 1 to 50 lines, described below. |
webhookUrlrequired | string | HTTPS URL that receives tariff_rate.completed. |
effectiveDateoptional | string | Default date for every line, YYYY-MM-DD. Defaults to today. |
loadingDateoptional | string | Loading date for time-based provisions, YYYY-MM-DD. |
modeOfTransportoptional | string | ocean, air, truck or rail. |
rates[] item
| Field | Type | Description |
|---|---|---|
htsCoderequired | string | HTS code, dots optional. |
originrequired | string | Country of origin, 2-letter ISO code. |
destinationrequired | string | Destination country, 2-letter ISO code. |
valueoptional | number | Line value in USD. |
dateoptional | string | This line's date, YYYY-MM-DD. |
metalsoptional | object | Section 232 content shares: { steel?, aluminum?, copper? }, 0 to 100. |
usContentPercentageoptional | number | US-origin content share, 0 to 100. |
steelCountryOfMeltoptional | string | Melt-and-pour country, 2-letter ISO code. |
aluminumCountryOfSmeltoptional | string | Smelt-and-cast country, 2-letter ISO code. |
uomQuantitiesoptional | object | Quantity per unit of measure, for specific duties. |
appliedExclusionsoptional | string[] | Chapter 99 exclusion codes to apply. |
chosenSpiCodesoptional | string[] | Special program indicators to evaluate. |
setKindoptional | string | For a set provision: how the set is rated. |
componentsoptional | object[] | For a set provision: up to 50 components { code, description?, valueShare?, quantity? }. |
curl -X POST "https://api.tarifflo.com/api/v1/tariff-rates" \
-H "X-API-Key: $TARIFFLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://your-server.com/webhooks/tarifflo",
"modeOfTransport": "ocean",
"rates": [
{ "htsCode": "7318.15.8060", "origin": "CN", "destination": "US", "value": 25000, "metals": { "steel": 100 } }
]
}'/api/v1/tariff-rates/{jobId}scope: tariff_ratesProgress while running; once complete, data holds one result per line, the same object the webhook carries.
Audit
Run a pre-audit over an entry's documents. Upload each file to a signed URL (the bytes never pass through our API), then start a run with the file ids. PDF, Excel, Word, CSV and images are accepted; a file whose content does not match its type is refused.
/api/v1/audit/uploadsscope: auditTakes filename and mimeType; returns fileId and uploadUrl, valid for 15 minutes. POST /api/v1/audit/uploads/bulk takes files (up to 50) and returns one upload per file.
/api/v1/audit/runsscope: auditBody
| Field | Type | Description |
|---|---|---|
configrequired | string | "pre" for a pre-audit. "post" (PSC) is not available yet. |
fileIdsrequired | string[] | 1 to 50 uploaded file ids. |
webhookUrloptional | string | Public HTTPS URL that receives the signed result. Omit it to poll the GET endpoint instead. |
clientNameoptional | string | Client the run is filed under in the dashboard. |
requestNameoptional | string | Name of the run in the dashboard. |
destinationCountriesoptional | string[] | 2-letter ISO codes. Defaults to US. |
/api/v1/audit/runs/{jobId}scope: auditStage progress while running; once complete, the same data as pre_audit.completed.
Export control
Determine the export-control classification (ECCN, or a foreign control code) of up to 50 products.
/api/v1/export-controlscope: eccnBody
| Field | Type | Description |
|---|---|---|
productsrequired | object[] | 1 to 50 products, described below. |
webhookUrloptional | string | Public HTTPS URL that receives the signed result. Omit it to poll the GET endpoint instead. |
products[] item
| Field | Type | Description |
|---|---|---|
descriptionrequired | string | What the product is and does, 3 to 5000 characters. |
originrequired | string | Country of origin, 2-letter ISO code. |
idoptional | string | Your reference, echoed on the result. |
nameoptional | string | Product name. |
contextoptional | string | Extra technical detail. |
/api/v1/export-control/{jobId}scope: eccnOrigin
Determine a finished good's country of origin under the rules of origin from its bill of materials. Each job takes minutes, so a request carries at most 5.
/api/v1/originscope: originBody
| Field | Type | Description |
|---|---|---|
jobsrequired | object[] | 1 to 5 determinations, described below. |
webhookUrloptional | string | Public HTTPS URL that receives the signed result. Omit it to poll the GET endpoint instead. |
jobs[] item
| Field | Type | Description |
|---|---|---|
finished_goodrequired | object | { description, hts?, value_usd?, ex_works_price_usd? } |
country_of_finished_productionrequired | string | Where the good was finished, 2-letter ISO code. |
bomrequired | object[] | 1 to 200 lines { item, origin, hts?, value_usd? }. |
import_intooptional | string | Importing country, 2-letter ISO code. |
claimed_country_of_originoptional | string | The origin you claim, to be checked. |
processingoptional | string | The manufacturing steps. |
idoptional | string | Your reference, echoed on the result. |
/api/v1/origin/{jobId}scope: originEntries
File an entry summary with CBP. The entry is validated in full first: if anything is wrong, the response lists every error by its path in your payload and nothing is saved. A valid entry is saved, encoded and sent to CBP in the same request.
/api/v1/entriesscope: entries:writeBody
| Field | Type | Description |
|---|---|---|
entryrequired | EntryApiInput | The entry summary. See Entry shape for every field. |
webhookUrloptional | string | Public HTTPS URL that receives entry.disposition when CBP answers. |
- Field reference: Entry shape.
- An
Idempotency-Keyheader is required. Repeating a key returns the first answer withIdempotent-Replayed: trueand never files twice. A repeat while the first request is still filing returns409 IDEMPOTENCY_KEY_IN_USE. - Entries need a live key for now: a test key (tflo_test_) is refused with 403 TEST_KEY_NOT_SUPPORTED.
- CBP's answer arrives later as an
entry.dispositionevent on your webhook. See Webhooks.
{
"efid": "0193c1f2-...",
"entryNumber": 1234567,
"shortJobId": "AB12CD",
"status": "AWAITING_CBP"
}{
"error": {
"code": "VALIDATION_ERROR",
"message": "The entry is not valid; nothing was filed",
"errors": [
{ "path": "lineItems[3].tariffRows[0].htsNumber", "message": "..." }
]
}
}If the entry was saved but could not be sent to CBP, the answer is 422 or 502 with code SUBMIT_FAILED and the efid of the saved entry.