Skip to main content

Verify and track

Declarants, importers and roadside officers do not have ITS accounts. Two public routes serve them, with no credential at all. You can build them into a national single window, a mobile app or a verification page.

What each route needs

The public routes are tiered by what the caller can prove, not by which application is calling:

The caller provesWho that isRouteWhat they get
The printed verification codeAnyone who has seen the document, or a photo of itGET /api/public/verify/{code}Whether the document is genuine, and its public state
The serial number and a party's TINThe declarant or the importer/consigneeGET /api/public/track/{serial_no} and /pdfThe corridor, the current holder, the guarantee verdict, tracking, and the printed leaflet
A bearer tokenAn administration/etd under the gatewayEverything in its scope, every version

Nothing public answers on a serial number alone. A serial encodes country, office and date, so an open route on it would let anyone walk the numbering.

Verify a printed document

GET /api/public/verify/{code}

The verification code is printed on the Transit Document and encoded in its QR code. It is 26 characters of Crockford base32, one per declaration, the same on every version. It is not guessable.

{
"status": "VALID",
"document_type": "ETD",
"issued_at": "2026-08-11T11:02:26.453195Z",
"document": {
"serial_no": "KEMBATD26081138",
"departure": { "country_code": "KE", "country_name": "Kenya" },
"destination": { "country_code": "UG", "country_name": "Uganda" },
"issued_at": "2026-08-11",
"state": "REGISTERED",
"version_no": 1,
"signature_status": "UNSIGNED",
"stamp_count": 1
}
}
statusHTTPMeaning
VALID200The document is genuine. Here is its public state.
REVOKED410The document was withdrawn. Do not rely on the paper.
UNKNOWN404No such code. Usually a typing mistake.
  • The code is case-insensitive, and the confusable characters I, L, O and U are accepted for 1 and 0.
  • The answer never carries names, values, HS codes, commodity descriptions, the guarantee, the corridor or where the goods are. A scan reveals no more than the paper already shows.
  • Responses carry Cache-Control: no-store.

Scanning the QR code

The QR code encodes a URL to a verification page, not a bare code. Take the last path segment as the code. Also accept a bare code, because older printed leaflets encode a different path.

Track a consignment

GET /api/public/track/{serial_no}
X-Party-Tin: <the declarant's or the consignee's TIN>

Only two TINs open a declaration:

  • the declarant's TIN (TD field 34), the person who lodged the declaration;
  • the importer/consignee's TIN (TD field 4).

The exporter's and the transporter's TINs are printed on the same document and are not accepted. Label the input "Declarant or consignee TIN".

Send the TIN in the X-Party-Tin header, never in the URL. Access logs record the request line, so a TIN in the path or the query would be written into every log between the caller and ITS.

{
"data": {
"serial_no": "UGEBBTD2608091",
"departure": { "country_code": "UG", "country_name": "Uganda" },
"destination": { "country_code": "KE", "country_name": "Kenya" },
"current_state": "IN_TRANSIT",
"stops": [
{
"stamp_type": "DEPARTURE",
"country_code": "UG",
"station_country": "UG",
"station_code": "EBB",
"next_country_code": "KE",
"received_at": "2026-08-11T14:02:26.45+03:00"
}
],
"current_holder": "UG",
"declared_next_country": "KE",
"versions": [
{
"version_no": 1,
"change_type": "ISSUANCE",
"created_at": "2026-08-11T14:02:26.45+03:00"
}
],
"guarantee_status": "VERIFIED",
"tracking": { "tracking_status": "NOT_TRACKED" }
}
}

The payload carries no names, no TINs, no commodity, no weight, no declared value and no guarantee amount or surety. guarantee_status is the verdict alone. tracking follows the same rules as for an administration: read tracking_status before position. See Reading and searching.

Every failure is the same 404

An unknown serial, a wrong TIN and the exporter's TIN instead of the consignee's all return one indistinguishable 404 (RES-ETD-043), in the same time. Do not guess which one happened. Say "We could not find a declaration matching those details."

A request with no X-Party-Tin header is 400 VAL-ETD-044.

Download the leaflet

GET /api/public/track/{serial_no}/pdf?leaflet=BOTH
X-Party-Tin: <the declarant's or the consignee's TIN>
  • It is always the current version. as_of and version numbers are not accepted here.
  • leaflet (I, II or BOTH), lang and disposition work as on the authenticated route. An unknown value is a 400.
  • Because X-Party-Tin is a custom header, a browser sends a CORS preflight, and the download cannot be a plain link. Fetch it and turn the response into a file.
  • Responses carry Cache-Control: no-store. Do not cache the bytes and do not send If-None-Match.

Serial number input

For an input placeholder, use the real format: UGEBBTD2608091. Accept 14 to 18 characters, and also the 23-character form issued before 14 August 2026, such as UGEBBTD0000000120260809.

Limits

A route with no credential is a target for automated guessing. Expect the public routes to be rate-limited, and back off when refused.