Classification

Classification proposes commodity codes for your goods. You hold the goods you want classified as items, optionally attach images of them, and then run a classification for a jurisdiction. A run is asynchronous: you start it and poll for its outcome.

Classification is licensed separately from the rest of CAS, so it has its own scopes. You can use CAS and classification together or either one on its own. Contact the Customs4trade Customer Success team to have classification enabled for your tenant.

Items

An item is the thing you want classified. It carries your own identifier, a description of the goods, an optional country of origin, any images you attach, and the codes assigned to it.

Two identifiers are involved, and it is worth keeping them apart:

Identifier What it is
externalReference Your own identifier for the item, unique within your tenant. You choose it, and you can search on it
id The technical ID CAS assigns to the item. This is the ID every item endpoint takes

Add an item

Use the Add a classification item endpoint:

POST https://api-<env>.customs4trade.com/v1/classifications/items
{
    "externalReference": "SKU-12345",
    "description": "100% cotton knitted t-shirt, men's, crew neck",
    "origin": "IN",
    "images": [
        { "url": "https://your-cdn.example.com/skus/12345/front.jpg" },
        { "contentType": "image/jpeg", "contentBase64": "/9j/4AAQSkZJRg...", "fileName": "front.jpg" }
    ],
    "assignedClassifications": [
        { "jurisdiction": "EU", "code": "6109100010", "comment": "Confirmed by trade compliance" },
        { "jurisdiction": "UK", "code": "6109100000" }
    ]
}

The response is 201 Created with the stored item, including the technical id you use from then on.

The more precise your description, the better the proposed codes. assignedClassifications lets you load an item that is already classified: each entry seeds the confirmed code for one jurisdiction, at most one entry per jurisdiction. It is not a classification run, so it starts no engine work and consumes no credits.

Scope: create:classifications

externalReference must be unique within your tenant. Adding a second item with a reference that is already in use returns 409 Conflict.

Search items

Use the Search classification items endpoint:

GET https://api-<env>.customs4trade.com/v1/classifications/items/search?offset=0&limit=50&externalReference=SKU-123*
Parameter Description
offset Number of items to skip. Defaults to 0
limit Number of items to return. Defaults to 50 and is capped at 200
externalReference Matches your own item identifier. * acts as a wildcard, so SKU-123* matches on the start, *123 on the end and *123* anywhere. Without a wildcard the value must match exactly
creationDateFrom, creationDateTo Inclusive range on when the item was created
lastUpdatedFrom, lastUpdatedTo Inclusive range on when the item was last changed
hasBetterSuggestion Only returns items where a proposed code scores better than the assigned one

No total count is returned, for performance. Page forward using links.next until it is empty:

{
    "data": [
        {
            "id": "0f1c2d3e-4a5b-6c7d-8e9f-a0b1c2d3e4f5",
            "externalReference": "SKU-12345",
            "description": "100% cotton knitted t-shirt, men's, crew neck",
            "origin": "IN"
        }
    ],
    "links": {
        "self": "/v1/classifications/items/search?offset=0&limit=50",
        "prev": null,
        "next": "/v1/classifications/items/search?offset=50&limit=50"
    }
}

Scope: read:classifications

Retrieve, update and delete an item

Use the Get classification item endpoint to read one item back with its images and assigned codes:

GET https://api-<env>.customs4trade.com/v1/classifications/items/{itemId}

Scope: read:classifications

Use the Update classification item endpoint to change an item's mutable fields:

PATCH https://api-<env>.customs4trade.com/v1/classifications/items/{itemId}

This applies JSON Merge Patch semantics, so send only what you want to change:

  • a field you leave out of the body is left unchanged;
  • a field with a value overrides it;
  • a field set to null clears it.
{
    "description": "100% cotton knitted t-shirt, men's, crew neck, updated spec",
    "origin": null
}

The example changes the description and clears the origin. externalReference is untouched because it was left out. An empty body ({}) is valid and changes nothing. Images are managed through the image endpoints and assigned codes come from classification runs, so neither can be changed here.

Scope: create:classifications

externalReference can be renamed but not cleared. Sending it as null or blank returns 400 Bad Request, and renaming it to a reference another item already uses returns 409 Conflict.

Use the Delete classification item endpoint to delete an item together with its images and its classification runs:

DELETE https://api-<env>.customs4trade.com/v1/classifications/items/{itemId}

The response is 204 No Content. Deleting an item that is already gone returns 404 Not Found.

Scope: delete:classifications

Images

Images improve the proposed codes considerably. CAS stores its own copy of every image and gives it an imageId. A url you supply is only used to fetch the bytes once and is not retained, so item responses carry image IDs rather than bytes or URLs.

There are three ways to attach an image:

Method When to use it
url in the item body CAS fetches the bytes for you. fileName defaults to the last segment of the url
contentBase64 in the item body Convenient for small images sent along with the item
Raw bytes to the image endpoint Best for larger files, as base64 inflates the payload by roughly a third

Add an image as raw bytes

Use the Add classification item image endpoint:

POST https://api-<env>.customs4trade.com/v1/classifications/items/{itemId}/images
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="front.jpg"

<raw image bytes>

Content-Disposition is optional and only used to keep the original file name. The response is 201 Created:

{
    "imageId": "img-5e6f7a8b",
    "itemId": "0f1c2d3e-4a5b-6c7d-8e9f-a0b1c2d3e4f5",
    "fileName": "front.jpg",
    "contentType": "image/jpeg"
}

The image is added to the item straight away, so your next classification run picks it up without any further action.

Scope: create:classifications

Image constraints

These apply to inline base64 and to raw bytes alike.

Constraint Value
Accepted content types image/jpeg, image/png, image/webp, image/gif, image/bmp and application/pdf
PDF handling Every page is rendered to an image and classified. Its images are named name (page N)
Maximum images per item request 50
Recommended size per image 10 MB or less. Larger images bring no gain in accuracy and slow processing down

Content that is not accepted, or that cannot be decoded, returns 400 Bad Request. A request that is too large returns 413 Payload Too Large.

Retrieve and remove an image

Use the Get classification item image endpoint to read the raw bytes of a stored image, served under the content type it was stored as:

GET https://api-<env>.customs4trade.com/v1/classifications/items/{itemId}/images/{imageId}

Scope: read:classifications

Use the Delete classification item image endpoint to remove a stored image:

DELETE https://api-<env>.customs4trade.com/v1/classifications/items/{itemId}/images/{imageId}

The response is 204 No Content, or 404 Not Found if the image ID is not known for that item.

Scope: delete:classifications

Running a classification

Start a run

Use the Start a classification endpoint:

POST https://api-<env>.customs4trade.com/v1/classifications/items/{itemId}/classifications
{
    "jurisdiction": "EU",
    "code": "6109100010"
}

jurisdiction is one of EU, UK, US or CH, and it must be enabled for your tenant. code is optional: supply it and the run verifies that code as well as proposing its own.

The response is 202 Accepted with the ID of the run:

{
    "classificationId": "9ab2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "itemId": "0f1c2d3e-4a5b-6c7d-8e9f-a0b1c2d3e4f5",
    "status": "Pending"
}

A run consumes one credit. When your credits are depleted the request returns 402 Payment Required and no run is started.

Scope: create:classifications

202 Accepted means the run was queued, not that it succeeded. Poll the run to get its outcome.

Poll for the result

Use the Get classification endpoint until status reaches Completed or Error:

GET https://api-<env>.customs4trade.com/v1/classifications/items/{itemId}/classifications/{classificationId}
Status Meaning
Pending Queued, not started yet
Processing Being classified
Completed Finished, result is populated
Error Failed, errorCode and message explain why

A completed run returns every candidate code it considered, the highest scoring first:

{
    "classificationId": "9ab2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "itemId": "0f1c2d3e-4a5b-6c7d-8e9f-a0b1c2d3e4f5",
    "status": "Completed",
    "jurisdiction": "EU",
    "result": {
        "notes": "General classification notes not tied to a specific option",
        "options": [
            {
                "code": "6109100010",
                "score": 9.2,
                "explanation": "Knitted cotton t-shirts fall under heading 6109"
            },
            {
                "code": "6105100010",
                "score": 4.1,
                "explanation": "Considered but the garment is a t-shirt, not a shirt"
            }
        ],
        "rulingReferences": [
            {
                "commodityCode": "6109100010",
                "url": "https://example.org/rulings/12345",
                "text": "T-shirts, singlets and other vests, knitted or crocheted, of cotton"
            }
        ]
    }
}

score is a confidence score from 0 to 10. A failed run reports the reason instead:

{
    "classificationId": "9ab2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "status": "Error",
    "errorCode": "classification_failed",
    "message": "No classification found"
}

Scope: read:classifications

Duty rates and measures are deliberately not part of the result.

Scopes

Classification uses its own scopes, so it can be enabled independently of the rest of CAS:

Scope Grants
read:classifications Searching and retrieving items, images and classification runs
create:classifications Adding and updating items, adding images, and starting classification runs
delete:classifications Deleting items and removing images

See Authentication for how to obtain a token.

Error handling

The classification endpoints return validation failures as RFC 9457 application/problem+json. Where more than one thing is wrong, every problem is reported at once so you can fix a whole batch in one round trip:

{
    "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "errors": {
        "images": [
            "images[1]: invalid base64 content."
        ],
        "assignedClassifications": [
            "jurisdiction 'XX' is not a known jurisdiction."
        ]
    }
}

This differs from the RFC 7807 invalid-parameters shape most other CAS endpoints use. See Error handling.

For HTTP status codes and error response formats, see Error handling.