Lifecycle and stamps
This guide explains how a declaration moves from lodgement to closure: the states, the four stamping acts, the rules that decide who may act, and cancellation.
The declaration and its versions
A declaration is lodged with POST /etd and corrected with POST /etd/{serial_no}/versions. Both take the whole document, grouped by the lettered sections of the TD form. The ITS API reference describes both operations and every field.
Two things matter for everything below:
- ITS generates the serial number. You never send one. Anything you send in
envelopeis ignored. - Every act appends a version. Lodgement, each stamp, each amendment, a cancellation and an attachment each add a new signed version. Expect
version_noto climb during ordinary movement, not only when someone corrects the document.
The serial number
A serial number such as KEMBATD26081138 reads as {country:2}{station:3}TD{yyMMdd}{counter}:
| Part | Example | Meaning |
|---|---|---|
| Country | KE | The departure country |
| Station | MBA | The departure customs office |
TD | TD | Literal: Transit Document |
| Date | 260811 | The issue date, from the ITS database clock |
| Counter | 38 | A per-office, per-day counter, not zero-padded |
The serial is 14 to 18 characters long. Read the counter as everything after character 13, never as a fixed slice. Serials issued before 14 August 2026 are 23 characters in an older layout, for example KEMBATD0000003820260811, and still resolve. Never validate a serial against one fixed length.
Every country performs the same three acts
There is no declared route. Each country acts on its own leg of the journey:
departure country REGISTER → DEPART → EXIT
transit country ARRIVE → DEPART → EXIT (once per country)
destination ARRIVE → [DEPART] → DISCHARGE
The departure country registers the declaration instead of arriving. The destination closes the transit instead of exiting. Implement one loop and the first and last countries differ only in which act stands in for which.
States
| State | Meaning |
|---|---|
REGISTERED | Lodged at the office of departure. The goods have not moved. |
DEPARTED | Released to run inland by the country that holds the goods. |
IN_TRANSIT | Between administrations: one country has stamped its exit, the next has not stamped entry. |
ARRIVED | A country's office of entry recorded the goods in its territory. |
DISCHARGED | The transit is closed. Terminal. |
CANCELLED | Withdrawn before the goods left the departure country. Terminal. |
ARRIVED means the goods entered whichever country they are now in, not only the destination. Read the corridor to see which country that is. See Reading and searching.
The four stamping acts
All four acts use one endpoint and one body shape:
POST /etd/{serial_no}/endorsements/{act}
| Act | Fires | From state | To state | next_country_code |
|---|---|---|---|---|
entry | ARRIVE | IN_TRANSIT | ARRIVED | Must be absent |
depart | DEPART | REGISTERED or ARRIVED | DEPARTED | Must be absent |
exit | EXIT | DEPARTED | IN_TRANSIT | Required |
discharge | DISCHARGE | ARRIVED or DEPARTED | DISCHARGED | Must be absent |
Every act needs the etd:endorse permission. An act name ITS does not know is refused with VAL-ETD-064, naming the four valid ones.
The request body
{
"data": {
"station": { "country_code": "KE", "station_code": "MBA" },
"officer": { "id": "KE-CUS-0001", "full_name": "A. Officer" },
"seal": { "type": "METALLIC", "no": "SEAL-0099124" }
}
}
| Field | Required | Notes |
|---|---|---|
station | Yes | The office where the act took place: country_code and a three-character station_code. |
officer | Yes | The officer's id and full_name, as your administration records them. |
seal | No | type (METALLIC or ELECTRONIC) and no. Both or neither. |
vehicle | No | A change of vehicle at this crossing. See Recording a change of vehicle. |
next_country_code | On exit only | The country the goods go to next. |
You never send a stamp time. ITS records when it received the stamp, by its own database clock. A stamp cut earlier but sent later sits later in the corridor.
A full call
curl --fail-with-body --silent --show-error \
--cert "$ITS_CLIENT_CERT" --key "$ITS_CLIENT_KEY" \
--header "Authorization: Bearer $ITS_TOKEN" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: 6f1c9a2e-3b7d-4c11-9f8a-2d5e7b0c4a13" \
--data '{
"data": {
"station": { "country_code": "KE", "station_code": "MBA" },
"officer": { "id": "KE-CUS-0001", "full_name": "A. Officer" },
"seal": { "type": "METALLIC", "no": "SEAL-0099124" },
"next_country_code": "UG"
}
}' \
"$ITS_GATEWAY_URL/etd/KEMBATD26081138/endorsements/exit"
The response
Every act answers 200 with the same shape:
{
"header": { "response_code": 200, "response_message": "SUCCESS" },
"data": {
"serial_no": "KEMBATD26081138",
"current_state": "IN_TRANSIT",
"transition_fired": true,
"operation": "EXIT",
"version_no": 3,
"content_hash": "9f2c..."
}
}
current_stateis the state after your call.version_noandcontent_hashdescribe the version your act appended.content_hashis theIf-Matchvalue your next amendment needs, so you do not have to read the document again first.transition_firedistruefor every act except one: an entry stamped at a one-stop border post before the previous country has exited. That call records the arrival and answerstransition_fired: falsewithoperation: null. It is not an error. The state catches up when the neighbour stamps its exit.header.additional_detailsis""when ITS has nothing to add. It can carry one or more remarks, each a complete sentence, for example when you stamp at an office your register does not list. A remark is never a refusal.
Every act needs an Idempotency-Key
Every stamp, every cancellation and every acknowledgement requires an Idempotency-Key header. Generate one value per logical act, for example a UUID, and send the same value again when you retry.
| Situation | Result |
|---|---|
| No key | 400 VAL-IDEM-001. Nothing reaches the declaration. |
| The same key and the same body | The first response is replayed. Nothing is recorded twice. |
| The same key while the first request is still running | 409 BUS-IDEM-002. Wait, then retry with the same key. |
| The same key with a different body | 422 VAL-IDEM-003. |
A timeout tells you nothing about whether a stamp landed. Retrying with the same key is the safe answer. Without a key, a retry would write a second stamp, append a second version and announce the movement to every other country a second time.
Keys are scoped to the request path, so the same key on two different declarations is two different requests.
Who may act, and when
The write turn
At any moment one country holds the declaration. ITS derives it from the stamps: the holder is the country with the earliest entry it has not yet closed with an exit or a discharge. Nobody stores it and nobody declares it.
| Act | Needs the write turn? |
|---|---|
entry | No. Entry is never gated. |
depart | It waits until the country behind you has stamped its exit. |
exit | Yes. Otherwise 403 AUTHZ-ETD-008. |
discharge | The destination country only. |
| Cancel | The departure country only, wherever the goods are. |
One-stop border posts
At a one-stop border post, such as Malaba, Busia or Chirundu, both administrations work in one building. The arriving country often stamps entry before the departing country stamps exit. Both orders are valid, so ITS never refuses an entry stamp for being early.
The ordering rule applies one act later, at depart:
You may always record that the goods reached you. You may not release them inland until the country behind you has stamped its exit.
Until then depart is refused with 409 BUS-ETD-063. The message names the country you are waiting on:
{
"header": {
"response_code": 409,
"response_message": "The goods cannot be released yet: the previous country has not stamped its exit.",
"additional_details": "Error code: BUS-ETD-063. CorrelationId: ... Error: cannot release the goods: ZW has not stamped its exit, and still holds this declaration"
}
}
Treat it as a wait, not a failure. Show who you are waiting on and retry later.
Exit: naming the next country
An exit hands the goods to the next country. next_country_code is required and must not be your own country. It decides who is told about the declaration, who may read it and where the corridor shows the goods heading.
- Re-routing needs nothing special. A different next country on a later exit is simply a different route.
- Name a country that is on ITS if one lies ahead of you. If the country you name has no system on ITS, the exit is still recorded and
additional_detailssays so. Name a connected country further along the route instead, if there is one. - The destination may not exit. It discharges instead (
VAL-ETD-007). - Exit is reachable only from
DEPARTED. A country cannot exit goods it never released. Calldepartfirst.
entry and exit also need a border office. If your customs office register records the station as INLAND, the stamp is refused with 409 BUS-ETD-083. An office nobody has described yet is accepted.
Discharge: closing the transit
A discharge closes the transit for good. It is its own act, not an exit with a field left off.
- Only the destination country may discharge.
- The stamp's station must be the declared office of destination. A discharge anywhere else is
409BUS-ETD-082, and the message names the office where the transit actually ends. - A discharge is reachable from
ARRIVED, when the office of destination is the border post the goods entered by, or fromDEPARTED, when it is an inland office reached after a release. - A discharge before the destination has stamped entry is
409BUS-ETD-009. Record the entry first. - Sending
next_country_codeon a discharge, an entry or a release is refused withVAL-ETD-007, not ignored.
If the goods must be cleared at a different office, the departure country amends the office of destination with POST /etd/{serial_no}/versions. The destination then discharges at the new office. Earlier versions keep the office they declared.
When the destination is not on ITS
A corridor can end in a country with no system on ITS. The goods still travel under the declaration, so:
- The last connected country stamps its exit as usual. The state is
IN_TRANSIT. - ITS sets a deadline from the declaration's own
authorization.transit_time_end. - Any later stamp withdraws the deadline.
- If the deadline passes and the destination still has no system on ITS, ITS closes the transit itself.
A transit closed this way is DISCHARGED, and its read carries discharge_kind: SYSTEM_DESTINATION_NOT_ONBOARDED. An officer's discharge reads OFFICER.
An officer's discharge means a named administration saw the goods at the office of destination. A SYSTEM_DESTINATION_NOT_ONBOARDED closure means ITS lost sight of the goods after they left for a country with no system on ITS. Never acquit a guarantee on it. See Reading and searching.
When the origin is not on ITS
The first connected country the goods reach lodges the declaration itself, as the departure country, naming one of its own offices. authorization.exporting_country can still name the real country of export.
Recording a change of vehicle
The means of transport is lodged once. When the goods change truck at a border, any of the four acts can carry the vehicle the office actually saw:
{
"data": {
"station": { "country_code": "UG", "station_code": "EBB" },
"officer": { "id": "UG-CUS-0001", "full_name": "A. Officer" },
"vehicle": { "plate_registration_no": "UAX 442K", "nationality": "UG" }
}
}
- Send both
plate_registration_noandnationality, or omitvehicleentirely. - It does not amend the lodged means of transport. If what was lodged was wrong, amend it.
- It is not searchable. Search by the serial number, the parties or the lodged plates.
Cancelling
POST /etd/{serial_no}/cancel
{ "data": { "comment": "issued in error" } }
- Only the departure country may cancel.
- You may cancel while the declaration is
REGISTEREDorDEPARTED, until any country stamps an exit. After that the transit is completed or discharged, never erased. A later attempt is409BUS-ETD-009. - The comment is optional, at most 500 characters.
- Send
Content-Type: application/jsoneven with an empty body, or the call is refused with415ERR-023. - Send an
Idempotency-Key, as for a stamp.
Checklist
- Call
departbetween registering or arriving and exiting. Anexitwithout it is refused. - Send an
Idempotency-Keyon every stamp and cancellation, and reuse it on a retry. - Do not sequence entry after the previous country's exit. Both orders are valid.
- Treat
BUS-ETD-063as a wait. - Send
next_country_codeonexitonly. - Close a transit with
discharge, never withexit. - Keep
content_hashfrom the response if you will amend next.