Draft a tariff classification memo
A classification memo with a proposed HTS code, the rules applied and word-for-word quotes from similar CBP rulings, or a clear 'needs a broker'.
Built on: Evidence retrieval, Signed record
Loading the tool…
Use it your way
Use it from your codeThe hosted API with your key, and prompts to paste into a coding agent
Get an API key
- Call the tariff classification memo API from your own code in minutes.
- Every model answer carries a signed receipt.
- Nothing to install; we run the models.
Run it yourself, on request
- The same open models and app, on 1x RTX PRO 6000 (96 GB) for Qwen3.8-27B; the embedder and reranker fit in about 10 GB beside it or on a second card; the checks and signing run on CPU.
- Data never leaves your machines, and there are no Decosa charges.
- One prompt for Claude Code or Codex assembles the whole stack.
- Early access: the container images are not public yet and the source needs access; the prompt says how to ask.
Build with it
Paste one of these into Claude Code, Codex or another coding agent. The first wires your project to the hosted API with your DECOSA_API_KEY. The second pulls our containers and runs the same stack on your own GPU, with no Decosa charges.
- Base URL
- https://api.decosa.ai
- Auth
Authorization: Bearer $DECOSA_API_KEY(or a demo session token)- Tool id
- tariff-classification
Use the hosted API
# Decosa tariff classification memo: use the hosted API
You are wiring Decosa's tariff classification memo into this project (a product-onboarding flow, a classification
worksheet or a broker's tool). Given a product description it returns CBP rulings on similar products with the passages
that matched, a proposed HTS heading, subheading and statistical number with the GRI applied and word-for-word quotes
(each checked in code), or "needs a broker" with the reason, plus a memo and a signed record. Every model call has a
signed receipt. Use only what is listed below; if you need something else, stop and ask me.
- Base URL: `https://api.decosa.ai`
- Health check: `GET https://api.decosa.ai/healthz`.
- It searches a subset of public CBP New York rulings (since 2018, 35 headings in 11 chapters) and one HTS release; see
`GET /tariff/info` for the set, its hash and the release.
- This is a research and drafting aid. A licensed customs broker or the importer of record decides the classification,
and only CBP issues binding rulings (19 CFR Part 177). Never file an entry on the memo alone.
- Do not send confidential product specifications to the hosted demo; self-host for those.
## Auth: API key (or a demo session)
1. Preferred: an API key (`dk_…`) from "Get an API key" on the tool page. Keep it in `DECOSA_API_KEY`, never in code.
Send `Authorization: Bearer $DECOSA_API_KEY`.
2. Without a key: `POST https://api.decosa.ai/demo/session` with `{"vertical": "tariff-classification"}` returns
`{"token", "expires_at", "budget"}`. A demo token runs one memo at a time (409 otherwise); over a limit you get 429
with `Retry-After`.
3. A memo needs about 1,400 generated tokens left in the budget before it starts (402 otherwise).
## Classify
- `POST /tariff/classify` (token). Body: `{"description": "30-4,000 characters: what it is, materials and their shares, how it is made, what it is for", "facts"?: "up to 2,000 characters", "title"?, "stream"?}` or `{"sample_id": "wall-heater"}`.
- The JSON answer has `status` (`proposed` or `needs_broker`), `reasons`, `proposal` (`heading`, `subheading`, `hts`,
`display`, `text`, `general_rate`, `alternatives`, `gri`, `reasoning`, `missing`, `confidence`, `ruling_citations`
with `ok` and byte offsets, `hts_citations`), `checks`, `rulings` (each with `excerpts` and byte offsets),
`search_receipts` (signed, naming `index_hash`), `memo_md`, `record`, `receipts` and `note`.
- With `Accept: text/event-stream` (or `"stream": true`) the events are `ready`, `rulings`, a `receipt` per model call,
`proposal`, `result`, `budget` and `done`.
- `POST /record/verify` (no token) `{"record": {...}}` re-checks the memo record. `GET /tariff/info`, `GET /tariff/samples`
and `GET /attest/signing-key` need no token.
## Example (Python, `pip install httpx`)
```python
import httpx, os
API = "https://api.decosa.ai"
H = {"Authorization": f"Bearer {os.environ['DECOSA_API_KEY']}"}
desc = ("Soft-sided insulated lunch bag; outer surface 600-denier polyester woven fabric; PEVA plastic liner over foam; "
"zipper, carry handle, shoulder strap; keeps a packed lunch cold.")
r = httpx.post(f"{API}/tariff/classify", json={"description": desc, "title": "Lunch bag SKU 1182"}, headers=H, timeout=180)
r.raise_for_status()
js = r.json()
print(js["status"], (js["proposal"] or {}).get("display"), js["reasons"])
for c in (js["proposal"] or {}).get("ruling_citations", []):
print(c["ruling"], "verified" if c["ok"] else "NOT VERIFIED", c["quote"])
open("memo.md", "w").write(js["memo_md"]) # for the broker
```
When `status` is `needs_broker`, show the reasons to a person and do not use the code.
Run it yourself (containers)
On request. The container images and the compose file aren’t public yet. Ask for self-host access and Decosa sends the registry (DECOSA_REGISTRY) and the compose file’s URL (DECOSA_COMPOSE_URL) these steps use. They are the steps we tested end to end on a fresh machine.
# Decosa tariff classification memo: run it yourself (containers)
You are setting up Decosa's tariff classification memo on this machine, so product descriptions never leave it. It
searches public CBP rulings and the HTS text with an open embedder and reranker, one open model (Qwen3.8-27B) proposes a
heading, subheading and statistical number with quotes, and code checks every code and quote. The output is a memo and a
signed record. Nothing is sent to Decosa's hosted API; the only outbound calls are the one-time downloads of the public
ruling set and the HTS.
Status: the container images (${DECOSA_REGISTRY}/decosa-*) and the compose file are on request while self-host is in early access (not on a public registry yet): ask at https://decosa.ai/contact?topic=self-host, and Decosa sends the registry as DECOSA_REGISTRY, the compose file URL as DECOSA_COMPOSE_URL, and pull access. If a pull fails with
"not found", "denied" or "unauthorized", stop and tell me. Do not substitute other images.
Ask me before any command that needs sudo, and show me the command first.
## Step 0: set up with a coding agent, rehearse on mock data, then go private
This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:
1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
"test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
https://decosa.ai/samples/tariff-classification.zip (2 KB, 9 checks, synthetic or openly licensed: see `licence` in expected.json),
show me what is in it, and run the rehearsal against the local API:
`docker compose exec api python scripts/rehearse.py tariff-classification` (the api image carries the same bundle under /app/rehearsal/tariff-classification/;
with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
`python scripts/rehearse.py tariff-classification --bundle tariff-classification.zip --base-url http://127.0.0.1:<PORT>`.
It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "the heater is proposed", "under heading 8516", "subheading 8516.29"). Show me
the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
machine.
For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.
## Steps
1. Docker: if `docker compose version` fails, install Docker Engine and the compose plugin using Docker's official
instructions for this distribution. Install the NVIDIA container toolkit, then check
`docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi`. I need one GPU with 64 GB or more
(or two GPUs).
2. Fetch the compose file: `mkdir -p ~/decosa && cd ~/decosa && curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml`.
Keep the `llm` and `api` services and add a `retrieval` service built from `services/retrieval` in the decosa-api
source (Qwen3-Embedding-0.6B and Qwen3-Reranker-4B, Apache-2.0; the full recipe is the tool's assemble prompt).
On the `api` service set `DECOSA_LLM_ROUTE=direct`, `DECOSA_LLM_URL=http://llm:8000/v1`, `DECOSA_LLM_MODEL=qwen3.8-27b`,
`DECOSA_RETRIEVAL_URL=http://retrieval:8499`, `DECOSA_TARIFF_DATA=/data/tariff` and `DECOSA_TARIFF_WARM=1`; keep its
data on a named volume and bind every port to 127.0.0.1.
3. `docker compose up -d --build`; wait for the health checks.
4. Fetch the ruling set and the HTS once: `docker compose exec api python scripts/tariff_fetch.py --out /data/tariff`,
then `docker compose restart api`. `GET /tariff/info` should show about 2,000 rulings and `retrieval.reachable: true`.
5. Smoke test: get a token with `POST /demo/session {"vertical":"tariff-classification"}`, then
`POST /tariff/classify {"sample_id":"wall-heater"}`. Expect heading 8516 with at least one verified ruling quote, every
receipt `attested`, and `POST /record/verify {"record": <record>}` giving `ok: true`. `{"sample_id":"vague-bag"}`
should come back `needs_broker`.
6. Report back: the public key (`GET /attest/signing-key`), the ruling-set count and index hash, and the smoke results.
A research and drafting aid: a licensed customs broker or the importer of record decides, and only CBP issues binding
rulings.
Off by default. Don't join as a provider on a box that holds unreleased product specifications.
Run it on your own hardwareWhat it needs, and the prompt that sets it up
Run it on your own GPU
Same app, same pinned models, your hardware. Nothing goes to our servers and there are no Decosa charges.
Hardware check
Check your own hardware- CPU only, 64 GB RAMDoesn't fit
Qwen3.8-27B (NVIDIA NVFP4) needs a GPU.
- GeForce RTX 4090Doesn't fit
Needs about 33 GB of GPU memory at the smallest settings; 24 GB available.
- GeForce RTX 5090Doesn't fit
Needs about 41 GB of GPU memory at the smallest settings; 32 GB available.
- 2x GeForce RTX 5090standard tierRuns
The standard tier fits with changes: Qwen3.8-27B (NVIDIA NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions.
- L40Sstandard tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA NVFP4) with Qwen3.8-27B official FP8. This build is NVIDIA NVFP4, which needs a Blackwell GPU.
- H100 80 GB (SXM)standard tierRuns
The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA NVFP4) with Qwen3.8-27B official FP8. This build is NVIDIA NVFP4, which needs a Blackwell GPU.
- RTX PRO 6000 Blackwell 96 GBstandard tierRuns
The standard tier fits (70.6 of 96 GB).
- 2x RTX PRO 6000 Blackwell 96 GBstandard tierRuns
The standard tier fits (70.6 of 192 GB).
- Apple M3 Ultra (Mac Studio), 96 GBCan't tell
Memory not known for Qwen3-Embedding-0.6B has no mapped Apple Silicon build; Qwen3-Reranker-4B has no mapped Apple Silicon build.
- Apple M5 Max, 64 GBCan't tell
Memory not known for Qwen3-Embedding-0.6B has no mapped Apple Silicon build; Qwen3-Reranker-4B has no mapped Apple Silicon build.
Memory per component comes from measured footprints, the tool's stack.json, or an estimate from its parameter count, and each is labelled that way below. Only an RTX PRO 6000 and an M3 Ultra Mac Studio have actually been run.
On request. The container images and the compose file aren’t public yet. Ask for self-host access and Decosa sends the registry (DECOSA_REGISTRY) and the compose file’s URL (DECOSA_COMPOSE_URL) these steps use. They are the steps we tested end to end on a fresh machine.
- 1
Check the GPU, Docker and the NVIDIA Container Toolkit
The driver must see the GPU, and Docker must be able to pass it into a container.
nvidia-smi docker compose version docker run --rm --gpus all ubuntu nvidia-smi
- 2
Fetch the compose file
One file describes the API and the language model as services.
mkdir -p ~/decosa && cd ~/decosa curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml - 3
Pull and start
The first start downloads pinned model weights, tens of gigabytes.
docker compose pull docker compose up -d
- 4
Check health
Wait until the API reports ok with the language model loaded. Then point your app at the local base URL.
curl -fsS http://localhost:<PORT>/healthz # {"ok": true, "llm": true, ...} curl -fsS -X POST http://localhost:<PORT>/demo/session \ -H 'Content-Type: application/json' -d '{"vertical":"tariff-classification"}'
Set up with a coding agent, rehearse on mock data, then go private
- Set up with a coding agent. Paste the self-host prompt into a coding agent on the machine that will run the service. We recommend Claude Code with Claude Opus 5.5; any capable coding agent works.
- Rehearse on mock data. The agent runs the tool on a bundle of synthetic inputs and checks each answer against the bundle's
expected.json. Every check must print PASS. - Go private. Only then do you run your own data against the local API, yourself, on that machine. Never give the agent real data during setup: a coding agent that runs in the cloud sees everything in its context, so keep real data out of the chat and out of the files it reads.
docker compose exec api python scripts/rehearse.py tariff-classification
Download the mock-data bundle (2 KB, 9 checks)expected.json
Two synthetic product descriptions. The wall-mounted, hard-wired fan heater must be proposed under heading 8516 (subheading 8516.29) with at least one quote verified word for word in a retrieved CBP ruling; the bag described only as a bag with a strap must go to a broker. The memo record must verify. Run it against your own ruling set: the ruling numbers you see depend on the set fetched.
What the rehearsal checks
- the heater is proposed
- under heading 8516
- subheading 8516.29
- at least one ruling quote verified word for word
- a retrieved ruling was classified by CBP in 8516
- the ruling search has a signed search receipt
- the memo record verifies
- the thin description goes to a broker
- every model call has a signed receipt
Licence: Synthetic descriptions written for decosa-api (AGPL-3.0-or-later). The rulings searched are CBP CROSS rulings and the HTS text, US Government works in the public domain.
Prompt for your coding agent
# Decosa tariff classification memo: run it yourself (containers)
You are setting up Decosa's tariff classification memo on this machine, so product descriptions never leave it. It
searches public CBP rulings and the HTS text with an open embedder and reranker, one open model (Qwen3.8-27B) proposes a
heading, subheading and statistical number with quotes, and code checks every code and quote. The output is a memo and a
signed record. Nothing is sent to Decosa's hosted API; the only outbound calls are the one-time downloads of the public
ruling set and the HTS.
Status: the container images (${DECOSA_REGISTRY}/decosa-*) and the compose file are on request while self-host is in early access (not on a public registry yet): ask at https://decosa.ai/contact?topic=self-host, and Decosa sends the registry as DECOSA_REGISTRY, the compose file URL as DECOSA_COMPOSE_URL, and pull access. If a pull fails with
"not found", "denied" or "unauthorized", stop and tell me. Do not substitute other images.
Ask me before any command that needs sudo, and show me the command first.
## Step 0: set up with a coding agent, rehearse on mock data, then go private
This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:
1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
"test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
https://decosa.ai/samples/tariff-classification.zip (2 KB, 9 checks, synthetic or openly licensed: see `licence` in expected.json),
show me what is in it, and run the rehearsal against the local API:
`docker compose exec api python scripts/rehearse.py tariff-classification` (the api image carries the same bundle under /app/rehearsal/tariff-classification/;
with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
`python scripts/rehearse.py tariff-classification --bundle tariff-classification.zip --base-url http://127.0.0.1:<PORT>`.
It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "the heater is proposed", "under heading 8516", "subheading 8516.29"). Show me
the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
machine.
For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.
## Steps
1. Docker: if `docker compose version` fails, install Docker Engine and the compose plugin using Docker's official
instructions for this distribution. Install the NVIDIA container toolkit, then check
`docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi`. I need one GPU with 64 GB or more
(or two GPUs).
2. Fetch the compose file: `mkdir -p ~/decosa && cd ~/decosa && curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml`.
Keep the `llm` and `api` services and add a `retrieval` service built from `services/retrieval` in the decosa-api
source (Qwen3-Embedding-0.6B and Qwen3-Reranker-4B, Apache-2.0; the full recipe is the tool's assemble prompt).
On the `api` service set `DECOSA_LLM_ROUTE=direct`, `DECOSA_LLM_URL=http://llm:8000/v1`, `DECOSA_LLM_MODEL=qwen3.8-27b`,
`DECOSA_RETRIEVAL_URL=http://retrieval:8499`, `DECOSA_TARIFF_DATA=/data/tariff` and `DECOSA_TARIFF_WARM=1`; keep its
data on a named volume and bind every port to 127.0.0.1.
3. `docker compose up -d --build`; wait for the health checks.
4. Fetch the ruling set and the HTS once: `docker compose exec api python scripts/tariff_fetch.py --out /data/tariff`,
then `docker compose restart api`. `GET /tariff/info` should show about 2,000 rulings and `retrieval.reachable: true`.
5. Smoke test: get a token with `POST /demo/session {"vertical":"tariff-classification"}`, then
`POST /tariff/classify {"sample_id":"wall-heater"}`. Expect heading 8516 with at least one verified ruling quote, every
receipt `attested`, and `POST /record/verify {"record": <record>}` giving `ok: true`. `{"sample_id":"vague-bag"}`
should come back `needs_broker`.
6. Report back: the public key (`GET /attest/signing-key`), the ruling-set count and index hash, and the smoke results.
A research and drafting aid: a licensed customs broker or the importer of record decides, and only CBP issues binding
rulings.
Off by default. Don't join as a provider on a box that holds unreleased product specifications.
Help me customise for my hardware
Pick your GPU or Mac, or enter its memory. You get the tier that fits, the model swaps it needs, measured speed where we have it, and a setup prompt with those choices written in.
GeForce RTX 5090: 32 GB GDDR7, 1,792 GB/s, FP8 and NVFP4. NVIDIA product page
Doesn't fitTariff classification memo on GeForce RTX 5090
Needs about 41 GB of GPU memory at the smallest settings; 32 GB available.
Lite · smaller reranker: what changesuses estimates
- Needs about 32.8 GB of GPU memory at the smallest settings; 32 GB available.
Memory per component
- One call per memo: Qwen3.8-27B (NVIDIA NVFP4). ~57.6 GB (at least ~28 GB), weights 21.4 GB (from stack.json). Qwen3.8-27B NVFP4: Weights 19.9 GiB (21.4 GB), measured (field stack.json). The compose file gives the server 0.60 of a 96 GB card (57.6 GB) so the rest is FP8 KV cache for several sessions. The 28 GB minimum is an estimate: weights plus a short-context KV cache, which is why several stacks list a 32 GB RTX 5090 as 'estimate'. (stack.json lists 57 GB for this component.)
- Embeds the ruling set once: Qwen3-Embedding-0.6B. ~2.4 GB, weights 1.2 GB (estimate). Estimate: 0.6B parameters at 2 bytes (BF16) per weight is about 1.2 GB, plus 20% working memory and 1 GB of runtime. Not measured.
- The smaller reranker for a card with less mem...: Qwen3-Reranker-0.6B. ~2.4 GB, weights 1.2 GB (estimate). Estimate: 0.6B parameters at 2 bytes (BF16) per weight is about 1.2 GB, plus 20% working memory and 1 GB of runtime. Not measured.
- Codes against the HTS release, codes nest, qu...: Checks and record (decosa-api, Python). CPU. Runs on CPU (vram_gb 0 in stack.json).
Expected speed
Not measured.
Not measured on this hardware. The only measured setups are an RTX PRO 6000 Blackwell and a Mac Studio M3 Ultra.
Setup prompt for this hardware
The self-host prompt for Tariff classification memo, with a hardware plan for GeForce RTX 5090 added after Step 0. Loading the full prompt; until then it points your agent at the prompt's URL.
# Set up Tariff classification memo on my hardware Fetch https://decosa.ai/prompts/tariff-classification-selfhost.md and follow it (including Step 0: rehearse on mock data first), with the hardware plan below applied. ## Hardware plan for this machine (from https://decosa.ai/self-host/hardware?use=tariff-classification) Target machine: GeForce RTX 5090 (32 GB of GPU memory; CUDA, FP8 and NVFP4). Quality tier: Lite · smaller reranker (lite). Fit check: doesn't fit; some memory numbers are estimates, not measurements. First, check the machine: run `nvidia-smi` (or `rocm-smi`, or `sysctl hw.memsize` on a Mac) and confirm the GPUs and free memory match the line above. If they do not, stop and tell me before pulling anything. Use these components (the setup below describes the standard tier; change it to match): - One call per memo: Qwen3.8-27B (NVIDIA NVFP4) (nvidia/Qwen3.8-27B-NVFP4), 57.6 GB - Embeds the ruling set once: Qwen3-Embedding-0.6B (Qwen/Qwen3-Embedding-0.6B), 2.4 GB - The smaller reranker for a card with less mem...: Qwen3-Reranker-0.6B (Qwen/Qwen3-Reranker-0.6B), 2.4 GB - Codes against the HTS release, codes nest, qu...: Checks and record (decosa-api, Python), CPU Warning: the fit check says this tier does not fit: Needs about 32.8 GB of GPU memory at the smallest settings; 32 GB available. Tell me before going further. During the rehearsal, watch GPU memory. If a model fails to load or runs out of memory, lower its --max-model-len and --max-num-seqs first, then its memory share, and tell me what you changed. The stack's own component list and compose layout: https://decosa.ai/prompts/tariff-classification-assemble.md
The proof
How we tested itEval results and end-to-end checks, hosted and self-hosted, with dates
Verified end to end
Hosted: verified 27 Sep 2026 · measured 27 Sep 2026: · p50 32 s · ~$0.004 per run · 1 receipt
Loading the nightly status…
Self-host: verified 27 Sep 2026 · fresh clone into a clean directory, the api image built from it, compose up (named volume), rehearsal bundle and the heater sample against the already-running local Qwen3.8-27B (direct route) and decosa-retrieval services
Measured cost to run: about $0.47 per 100 products (hosted, 27 Sep 2026, partly estimated). Self-hosting is free: the code is open and the models are open-weight. You pay only for your own hardware and power.
9/9 rehearsal checks with the bundled 198-ruling sample set; receipts attested. The retrieval container build and the one-hour ruling fetch in the assemble prompt were not re-run in the sandbox (no new GPU loads; the fetch ran on the host).
Known limits (4)
- Hosted verification ran on the pre-release server (decosa-api the pre-release branch on our server, gateway route). The production API gets this tool when the branch merges.
- The eval asks about products CBP already ruled on, described in CBP's own words; real product sheets are vaguer, so expect more 'needs a broker'.
- Older rulings cite statistical numbers that no longer exist; the memo keeps the valid 8- or 6-digit prefix and flags it.
- 1,992 rulings in 35 headings; a product outside those chapters gets thin evidence and goes to a broker.
How it's builtThe steps, the models and what each one checks
Get an API key
- Call the tariff classification memo API from your own code in minutes.
- Every model answer carries a signed receipt.
- Nothing to install; we run the models.
Run it yourself, on request
- The same open models and app, on 1x RTX PRO 6000 (96 GB) for Qwen3.8-27B; the embedder and reranker fit in about 10 GB beside it or on a second card; the checks and signing run on CPU.
- Data never leaves your machines, and there are no Decosa charges.
- One prompt for Claude Code or Codex assembles the whole stack.
- Early access: the container images are not public yet and the source needs access; the prompt says how to ask.
A product description in; CBP rulings on similar products, a proposed HTS code with the GRI and word-for-word quotes, or a clear 'needs a broker', out.
For customs brokers and importers' trade-compliance teams. It searches public CBP New York rulings and the HTS text of the candidate headings, an open model proposes a heading, subheading and statistical number with the General Rules of Interpretation it applied, and code checks every code against the HTS release and every quote against the ruling text it retrieved. When a fact the code turns on is missing, or the evidence is thin, it says so. A research aid: a licensed broker decides.
- Deployment
- Hosted or self-host
- Regulatory
- Checked 27 Sep 2026. Importers must use reasonable care to classify (19 U.S.C. 1484, https://www.law.cornell.edu/uscode/text/19/1484); binding rulings come only from CBP under 19 CFR Part 177 (https://www.ecfr.gov/current/title-19/chapter-I/part-177), and a ruling binds only the transaction it describes. Customs business, including classification for others, is for licensed customs brokers (19 U.S.C. 1641, https://www.law.cornell.edu/uscode/text/19/1641). The General Rules of Interpretation are quoted from the HTS (USITC, 2026 Revision 19, https://hts.usitc.gov). The memo is not a ruling, not advice, and not a filing; Chapter 99 duties (Section 301, 232, IEEPA) are not applied.
Text description
A product description goes to decosa-api. The evidence retrieval block searches a hashed snapshot of public CBP rulings and the HTS heading texts with Qwen3-Embedding-0.6B and Qwen3-Reranker-4B (Apache-2.0), returning chunks with byte offsets and a signed search receipt. Qwen3.8-27B (Apache-2.0) reads the rulings, the heading texts and the GRI and proposes a code with quotes. Code checks every code against the HTS release and every quote word for word, and anything unsupported goes to a broker. Outputs: a memo for a licensed broker and a signed, hash-chained record. Hosted, model calls get gateway receipts; self-hosted, everything stays on your machine.
At a glance
- What it gives you
- A memo: the rulings found (with the matching passages), a proposed heading, subheading and statistical number with the GRI applied and verified quotes, up to two alternatives, the facts a broker should confirm, and a signed record naming the ruling set and the index searched.
- Coverage
- It covers CBP New York rulings in 35 headings of 11 chapters. Chapters: 39, 42, 61, 62, 63, 64, 73, 84, 85, 94, 95. Headings: 3923, 3924, 3926, 4202, 6104, 6109, 6110, 6114, 6204, 6211, 6307, 6402, 6403, 6404, 7323, 7326, 8414, 8419, 8471, 8479, 8481, 8504, 8516, 8517, 8518, 8528, 8543, 8544, 9401, 9403, 9405, 9503, 9504, 9505, 9506. A product outside them comes back as "out of coverage", naming the heading it would need; a licensed customs broker can classify it. Inside them, a description that lacks a deciding fact comes back as "needs a broker".
- What it does not do
- It does not decide, file or rule: a licensed customs broker or the importer of record decides, and only CBP issues binding rulings. It searches 1,992 New York rulings in 35 headings, not all of CROSS, and not HQ rulings, court cases or the Explanatory Notes. It does not apply Chapter 99 duties or decide origin or value.
- Data retention
- Nothing stored: the description lives in memory for the request. Logs carry counts and timings only.
- Evidence it keeps
- The record commits to the description's hash, the ruling-set hash, the HTS release, the retrieval index hash (a Merkle root over every chunk), the search and model receipts, the proposal and every check.
Pick the tier for the quality you need
Same app at every tier. What changes is the models, the hardware they need, and whether receipts are signed. Scores are measured with the source named, or marked not measured.
Lite
smaller reranker
Qwen3-Reranker-0.6B instead of the 4B: about 7 GB less GPU memory. Not measured on this tool.
- Models
- Qwen3.8-27B (NVIDIA NVFP4)
- Qwen3-Embedding-0.6B
- Qwen3-Reranker-0.6B
- Checks and record (decosa-api, Python)
- Hardware
- 1x 80-96 GB card
- Quality evidence
- held-out accuracy with the 0.6B rerankernot measured yet
- Latency
- not measured
- Verification
- Proof: strongSelf-host onlySame receipts as standard.
- In the hosted demo
Standard
the hosted demo
Hybrid search with the 4B reranker, one Qwen3.8-27B call, the checks and the signed record.
- Models
- Qwen3.8-27B (NVIDIA NVFP4)
- Qwen3-Embedding-0.6B
- Qwen3-Reranker-4B
- Checks and record (decosa-api, Python)
- Hardware
- 1x RTX PRO 6000 Blackwell 96 GB (or the LLM and retrieval on two cards)
- Quality evidence
- memo top-1 subheading (6-digit), held-out rulings (n=200, run once)145 / 200decosa-api docs/evals/tariff-classification.md, 2026-09-27
- memo top-1 heading (4-digit), held-out171 / 200decosa-api docs/evals/tariff-classification.md, 2026-09-27
- memo top-3 subheading, held-out172 / 200decosa-api docs/evals/tariff-classification.md, 2026-09-27
- memos proposed (not sent to a broker) and their subheading accuracy, held-out108 / 200 proposed; 81% rightdecosa-api docs/evals/tariff-classification.md, 2026-09-27
- same model without retrieval, top-1 subheading, held-out57 / 200decosa-api docs/evals/tariff-classification.md, 2026-09-27
- ruling vote alone (hybrid + rerank) / BM25 alone, top-1 subheading, held-out118 / 200 / 116 / 200decosa-api docs/evals/tariff-classification.md, 2026-09-27
- Latency
- measured on our server: about half a minute per memo on shared GPUs
- Verification
- Proof: strongGateway-signed receipt for the model call; model-call receipts signed by decosa-api for the embedder and reranker; a signed search receipt with the index hash.
Every model in the stack
| Model | Tiers | Params · VRAM | Verification | Details |
|---|---|---|---|---|
One call per memo: reads the rulings found, the HTS text of the candidate headings and the GRI, and proposes a heading, subheading and statistical number with quotes, or declines.Qwen3.8-27B (NVIDIA NVFP4)nvidia/Qwen3.8-27B-NVFP4 on Hugging Face (opens in a new tab) 27.8B · 57 GBProof: strongIn the hosted demo | LiteStandard | 27.8B · 57 GB | Proof: strongIn the hosted demo | |
| ||||
Embeds the ruling set once (cached) and each product description, for the dense half of the hybrid search (BM25 is the other half).Qwen3-Embedding-0.6BQwen/Qwen3-Embedding-0.6B on Hugging Face (opens in a new tab) 0.6B · about 1.2 GB (estimate)Proof: partialIn the hosted demo | LiteStandard | 0.6B · about 1.2 GB (estimate) | Proof: partialIn the hosted demo | |
| ||||
Scores the 40 best chunks for each description, so the rulings the model reads are the closest products, not the closest words.Qwen3-Reranker-4BQwen/Qwen3-Reranker-4B on Hugging Face (opens in a new tab) 4B · about 8.1 GB (estimate)Proof: partialIn the hosted demo | Standard | 4B · about 8.1 GB (estimate) | Proof: partialIn the hosted demo | |
| ||||
The smaller reranker for a card with less memory.Qwen3-Reranker-0.6BQwen/Qwen3-Reranker-0.6B on Hugging Face (opens in a new tab) 0.6B · about 1.2 GB (estimate)Proof: partialSelf-host only | Lite | 0.6B · about 1.2 GB (estimate) | Proof: partialSelf-host only | |
| ||||
Codes against the HTS release, codes nest, quotes word for word with byte offsets, a cited ruling at the proposed heading, rulings agree, evidence not thin; the signed record.Checks and record (decosa-api, Python) 0 GBNo proof yetIn the hosted demo | LiteStandard | 0 GB | No proof yetIn the hosted demo | |
| ||||
How often is the proposed code right?
Held-out CBP New York rulings: the product description from each ruling (tariff numbers masked, the classification paragraphs cut) against the code CBP gave. The index never holds the held-out rulings or near duplicates of them. Prompts and checks were set on 60 dev rulings; the 200 test rulings were run once.
- Memo, top-1 heading / subheading
- 86% / 72%n = 200; top-3 subheading 86%
- Proposed memos only
- 81% subheading right108 of 200 proposed; the rest sent to a broker with the reason
- Same model, no retrieval
- 50% / 28%heading / subheading, top-1
- Rulings' vote, no model (hybrid + rerank / BM25)
- 59% / 58%subheading, top-1
Retrieval is most of the gain
The same model with no rulings to read gets the subheading right far less often; with them it beats the rulings' own vote because it reads the product against the heading texts.
It declines a lot
Many memos go to a broker, usually because the description lacks a fact the line turns on (fibre shares, gender, essential character) or a quote could not be verified. That is the intended direction of error, and it costs coverage.
Source: decosa-api docs/evals/tariff-classification.md, 2026-09-27; docs/evals/tariff-classification/test-score.json
Tools, services and hardware
Tools
- CBP CROSS rulings (opens in a new tab)US Government work, public domain (17 U.S.C. 105)
1,992 New York classification rulings since 2018 in 35 headings of 11 chapters, fetched 27 Sep 2026 (scripts/tariff_fetch.py); addressee and salutation removed.
- Harmonized Tariff Schedule (USITC REST export) (opens in a new tab)US Government work, public domain
Headings and subheadings of the 11 chapters, and the General Rules of Interpretation, 2026 Revision 19.
Services
- decosa-api:8445
${DECOSA_REGISTRY}/decosa-api:0.1.0The ruling set and HTS, the checks, the record and the HTTP API (/tariff/*). No GPU.
- decosa-retrieval:8499
The evidence retrieval block's embedder and reranker (services/retrieval). Internal only.
- decosa-llm:8000
${DECOSA_REGISTRY}/decosa-llm:0.1.0vLLM OpenAI endpoint for Qwen3.8-27B. Internal only.
Hardware
- 1x RTX PRO 6000 Blackwell 96 GB Fits
Measured on our server: the LLM on one card, the embedder and reranker (12.4 GB peak) on the other; both fit one card by the numbers (57 + 12.4 GB), not measured together.
- Two 48 GB cards
Not measured: FP8 LLM on one, retrieval on the other.
Latency per lane
- one memo, hosted (search + one model call), busy shared GPUs32.1 s
Measuredmeasured on our server 2026-09-27, pre-release server, gateway route
- index the 1,992 rulings (about 10,000 chunks), once per ruling-set version141.8 s
Measuredmeasured on our server 2026-09-27; cached on disk afterwards
Run this exact stack on your machine
Paste into Claude Code / Codex to assemble this stack locally. The prompt checks your GPU, pulls the pinned models, writes the compose file and runs a smoke test.
# Assemble the Decosa tariff classification memo on this machine
You are setting up a self-hosted tariff classification memo on this Linux machine for a customs broker or an importer's
trade-compliance team. Given a product description, it searches public CBP rulings (CROSS, New York classification
rulings) and the HTS text, and returns:
- a proposed HTS heading, subheading and statistical number with the General Rules of Interpretation applied, up to two
alternatives, and quotes from the rulings and the HTS, each quote checked word for word in code; or "needs a broker"
with the reason (a missing fact, thin evidence, rulings that disagree, a quote that could not be verified);
- a memo in Markdown and a signed, hash-chained record naming the ruling-set version, the HTS release and the retrieval
index hash.
Work step by step, show me each command before running anything that needs sudo, and stop if a check fails.
**Before anything else, remind me:**
- Product descriptions stay on this machine. The only outbound calls are the one-time downloads of the public ruling set
(rulings.cbp.gov) and the HTS (hts.usitc.gov); after that it can run offline.
- This is a research and drafting aid. A licensed customs broker or the importer of record decides the classification,
and only CBP issues binding rulings (19 CFR Part 177).
Repeat both points in your final summary.
## Step 0: set up with a coding agent, rehearse on mock data, then go private
This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:
1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
"test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
https://decosa.ai/samples/tariff-classification.zip (2 KB, 9 checks, synthetic or openly licensed: see `licence` in expected.json),
show me what is in it, and run the rehearsal against the local API:
`docker compose exec api python scripts/rehearse.py tariff-classification` (the api image carries the same bundle under /app/rehearsal/tariff-classification/;
with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
`python scripts/rehearse.py tariff-classification --bundle tariff-classification.zip --base-url http://127.0.0.1:<PORT>`.
It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "the heater is proposed", "under heading 8516", "subheading 8516.29"). Show me
the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
machine.
For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.
## What you are building
| service | image | model | port |
|---|---|---|---|
| `llm` | `${DECOSA_REGISTRY}/decosa-llm:0.1.0` (vLLM 0.29.0) | `nvidia/Qwen3.8-27B-NVFP4` @ `482ca0f3832238542f8f5295dde86b5f22711d80`, Apache-2.0 | internal 8000 |
| `retrieval` | built here from `services/retrieval` (PyTorch, CUDA) | `Qwen/Qwen3-Embedding-0.6B` + `Qwen/Qwen3-Reranker-4B`, Apache-2.0 (revisions pinned in `services/retrieval/models.py`) | internal 8499 |
| `api` | `${DECOSA_REGISTRY}/decosa-api:0.1.0` (no GPU) | none | `127.0.0.1:8445` |
## 1. Check the GPU, driver and Docker
1. Run `nvidia-smi`. I need one NVIDIA GPU with at least 64 GB (the LLM plus about 10 GB for the embedder and reranker),
or two GPUs (LLM on one, retrieval on the other), driver 580 or newer. Blackwell uses the NVFP4 build (measured);
Hopper: set `LLM_MODEL=Qwen/Qwen3.8-27B-FP8` and `LLM_REVISION=main` (not measured).
2. Check `docker compose version` and `docker run --rm --gpus all ubuntu nvidia-smi`. If Docker or the NVIDIA Container
Toolkit is missing, install them from the official repositories, run `sudo nvidia-ctk runtime configure --runtime=docker`
and restart Docker.
3. Confirm about 60 GB of free disk.
## 2. Get the source and the images
The images are **on request** while self-host is in early access: ask at https://decosa.ai/contact?topic=self-host, and Decosa sends the registry (set it as `DECOSA_REGISTRY`), pull access and the compose file. Try `docker pull ${DECOSA_REGISTRY}/decosa-{llm,api}:0.1.0`; if that
fails, build from the decosa-api source (`docker build -f docker/api/Dockerfile -t ${DECOSA_REGISTRY}/decosa-api:0.1.0 .`).
Clone decosa-api either way: the retrieval service is built from it. If neither works, stop and tell me.
Create `services/retrieval/Dockerfile` in the clone if it is not there:
```dockerfile
FROM pytorch/pytorch:2.9.0-cuda12.8-cudnn9-runtime
WORKDIR /app
COPY services/retrieval/ /app/
RUN pip install --no-cache-dir "transformers>=4.57,<6" "sentence-transformers>=5.1" "fastapi>=0.115" "uvicorn>=0.30" numpy
ENV RETRIEVAL_HOST=0.0.0.0 RETRIEVAL_PORT=8499 HF_HOME=/models
CMD ["python", "server.py"]
```
## 3. Write the compose file
`~/decosa-tariff/.env`:
```bash
DECOSA_TAG=0.1.0
LLM_MODEL=nvidia/Qwen3.8-27B-NVFP4
LLM_REVISION=482ca0f3832238542f8f5295dde86b5f22711d80
LLM_GPU_UTIL=0.70
DECOSA_SIGNER_NAME="<who signs the memo records, e.g. Example Customs Brokerage>"
```
`~/decosa-tariff/docker-compose.yml` (set `context` to the clone's path):
```yaml
name: decosa-tariff
x-health: &health { interval: 15s, timeout: 5s, retries: 5 }
services:
llm:
image: ${DECOSA_REGISTRY}/decosa-llm:${DECOSA_TAG}
deploy: { resources: { reservations: { devices: [ { driver: nvidia, device_ids: ["0"], capabilities: [gpu] } ] } } }
ipc: host
restart: unless-stopped
volumes: [hf-cache:/root/.cache/huggingface]
command: ["${LLM_MODEL}", "--revision", "${LLM_REVISION}", "--served-model-name", "qwen3.8-27b", "--language-model-only",
"--max-model-len", "65536", "--gpu-memory-utilization", "${LLM_GPU_UTIL}", "--kv-cache-dtype", "fp8_e4m3",
"--seed", "0", "--enable-force-include-usage", "--host", "0.0.0.0", "--port", "8000"]
healthcheck: { <<: *health, test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=4)"], start_period: 900s }
retrieval:
build: { context: /path/to/decosa-api, dockerfile: services/retrieval/Dockerfile }
deploy: { resources: { reservations: { devices: [ { driver: nvidia, device_ids: ["0"], capabilities: [gpu] } ] } } }
restart: unless-stopped
environment: { RETRIEVAL_EMBED: qwen3-emb-0.6b, RETRIEVAL_RERANK: qwen3-rr-4b }
volumes: [retrieval-models:/models]
healthcheck: { <<: *health, test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8499/health', timeout=4)"], start_period: 600s }
api:
image: ${DECOSA_REGISTRY}/decosa-api:${DECOSA_TAG}
restart: unless-stopped
depends_on: { llm: { condition: service_healthy }, retrieval: { condition: service_healthy } }
environment:
DECOSA_LLM_ROUTE: direct # local model; receipts signed by this box's key ("attested")
DECOSA_LLM_URL: http://llm:8000/v1
DECOSA_LLM_MODEL: qwen3.8-27b
DECOSA_RETRIEVAL_URL: http://retrieval:8499
DECOSA_TARIFF_DATA: /data/tariff # the ruling set and the index cache live in the data volume
DECOSA_TARIFF_WARM: "1" # build the ruling index at start-up
DECOSA_LOCAL_SIGNING: "on"
DECOSA_SIGNER_NAME: ${DECOSA_SIGNER_NAME}
DECOSA_SESSIONS_PER_IP_HOUR: "1000"
DECOSA_BUDGET_LLM_TOKENS: "200000"
ports: ["127.0.0.1:8445:8445"]
volumes: [decosa-data:/data]
healthcheck: { <<: *health, test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8445/healthz', timeout=4)"], start_period: 20s }
volumes: { hf-cache: {}, retrieval-models: {}, decosa-data: {} }
```
Use the named volumes exactly as written (a root-owned bind mount breaks `/data`). Run `docker compose up -d --build` and
poll `docker compose ps` until all three are healthy. The first start downloads about 20 GB of LLM weights and 9 GB of
retrieval weights.
## 4. Fetch the ruling set and the HTS
Without this step the API uses the small sample set bundled with it (fine for the smoke test, too small for real use).
```bash
docker compose exec api python scripts/tariff_fetch.py --out /data/tariff # about 1 hour at 2 requests a second
docker compose restart api # the index builds at start-up (a few minutes)
curl -s localhost:8445/tariff/info | jq '{ruling_set, hts, index, retrieval}'
```
`ruling_set.count` should be about 2,000 and `retrieval.reachable` true. Re-run the fetch to refresh; the ruling-set hash
and the index hash in every memo then change, which is the point.
## 5. Smoke test
```bash
API=localhost:8445
TOKEN=$(curl -s $API/demo/session -H 'content-type: application/json' -d '{"vertical":"tariff-classification"}' | jq -r .token)
curl -s $API/tariff/classify -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{"sample_id":"wall-heater"}' > /tmp/memo.json
jq '{status, code: .proposal.display, reasons, quotes: [.proposal.ruling_citations[] | {ruling, ok}], rulings: [.rulings[].ruling]}' /tmp/memo.json
jq '{record}' /tmp/memo.json | curl -s $API/record/verify -H 'content-type: application/json' -d @- | jq '{ok, summary}'
curl -s $API/tariff/classify -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' -d '{"sample_id":"vague-bag"}' | jq '{status, reasons}'
```
Pass if the heater memo proposes heading 8516 with at least one verified ruling quote, every receipt is `attested`, the
record verifies, and the vague bag comes back `needs_broker`.
## 6. Use it
- `POST /tariff/classify {"description": "...", "facts": "...", "title": "..."}` returns JSON, or Server-Sent Events with
`Accept: text/event-stream`. `GET /tariff/info` lists the ruling set, the HTS release, the models and the limits.
- Save `memo_md` and `record` in the product's classification file. Anyone can re-check the record with
`POST /record/verify` against the key at `GET /attest/signing-key`.
- Keep the API on 127.0.0.1; for other users, put a TLS reverse proxy with authentication in front.
## 7. Keep the direct route
`DECOSA_LLM_ROUTE=gateway` would send prompts (your product description and the rulings) to the hosted Decosa
API. Leave it
off for unreleased products.
Finish with a summary: what is running, the health output, the ruling-set count and index hash, the smoke-test results,
and the reminders above.Rules and regulations it checks againstDated, linked to the primary source; not legal advice
Regulation watch
Loading the watch status…
4 laws, rules and guidance pages cited; 4 watched nightly at the primary source. A change marks this page for a human re-check; nothing is edited automatically. What we cite and how it is watched
Technical detailsModels, where it runs, labels
In short
Last reviewed
- What it is
- A research aid for HTS classification: describe a product and it finds CBP rulings on similar products, proposes a heading, subheading and statistical number with the GRI applied, and quotes the rulings word for word. A licensed customs broker decides.
- Who it's for
- Licensed customs brokers and importers' trade-compliance teams who write classification memos for new products.
- Where it runs
- Hosted or self-host
- Key numbers
On 200 held-out CBP rulings, the memo's top-1 subheading matched CBP's 145 / 200 times, against 57 / 200 for the same model without the rulings; queries were CBP's own descriptions.
- 145 / 200 Memo top-1 subheading (6-digit) (test split, n = 200)
- 171 / 200 Memo top-1 heading (4-digit) (test split, n = 200)
- 172 / 200 Memo top-3 subheading (test split, n = 200)
- 32.1 s Median end-to-end run, hosted (QA sweep 2026-09-27)
- Models
- Qwen3.8-27B (the proposal); Qwen3 embedder and reranker (the search over rulings)
- Where
- Hosted or self-host
- Checks
- Receipt per model call; signed search receipts with the ruling-set index hash; every ruling quote verified word for word with its byte offsets; codes checked against the HTS release; signed hash-chained memo record
- Industry
- Compliance and trust
- Input
- Text and documents
- Output
- Signed record or verdict · Notes, reports and drafts
- Data
- Confidential business data
- Hardware
- 1× 96 GB GPU
- Licence
- Permissive (Apache-2.0, MIT)
- Runs in
- Decosa hosted · Self-host
- Built from
- Evidence retrieval · Signed record
Questions people ask
Can it do HTS classification for me?
It drafts a memo: a proposed heading, subheading and statistical number, the General Rules of Interpretation applied, and quotes from CBP rulings on similar products. A licensed customs broker or the importer of record decides, and only CBP issues binding rulings.
How accurate is it?
On 200 held-out CBP New York rulings, the top-1 subheading matched CBP's code 145 / 200 times and the heading 171 / 200. The same model with no rulings to read got the subheading 57 / 200. The queries were CBP's own product descriptions, so real product sheets will do worse.
Which rulings does it search?
1,992 New York classification rulings since 2018 from CBP's CROSS database, in 35 headings of 11 chapters, fetched on 27 Sep 2026. The memo record names the ruling set's hash and the index hash, so you can show which version it searched.
How do I know the quotes are real?
Every quote from a ruling is checked in code against the text the search returned, and the memo shows its byte offsets in that ruling. A quote that is not found word for word sends the memo to a broker.
When does it refuse to propose a code?
When the description lacks a fact the code turns on (the outer-surface material, fibre shares by weight, knit or woven, gender), when the closest rulings disagree, when the evidence is thin, or when a quote or code fails a check. It says which.
Ask a question or leave feedbackWe read every message and publish useful answers
Ask about Tariff classification memo
We read every message. Questions, comments and our answers show here once we have reviewed and approved them.
Loading questions…