Skip to main content

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 envelope is ignored.
  • Every act appends a version. Lodgement, each stamp, each amendment, a cancellation and an attachment each add a new signed version. Expect version_no to 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}:

PartExampleMeaning
CountryKEThe departure country
StationMBAThe departure customs office
TDTDLiteral: Transit Document
Date260811The issue date, from the ITS database clock
Counter38A 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

StateMeaning
REGISTEREDLodged at the office of departure. The goods have not moved.
DEPARTEDReleased to run inland by the country that holds the goods.
IN_TRANSITBetween administrations: one country has stamped its exit, the next has not stamped entry.
ARRIVEDA country's office of entry recorded the goods in its territory.
DISCHARGEDThe transit is closed. Terminal.
CANCELLEDWithdrawn 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}
ActFiresFrom stateTo statenext_country_code
entryARRIVEIN_TRANSITARRIVEDMust be absent
departDEPARTREGISTERED or ARRIVEDDEPARTEDMust be absent
exitEXITDEPARTEDIN_TRANSITRequired
dischargeDISCHARGEARRIVED or DEPARTEDDISCHARGEDMust 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" }
}
}
FieldRequiredNotes
stationYesThe office where the act took place: country_code and a three-character station_code.
officerYesThe officer's id and full_name, as your administration records them.
sealNotype (METALLIC or ELECTRONIC) and no. Both or neither.
vehicleNoA change of vehicle at this crossing. See Recording a change of vehicle.
next_country_codeOn exit onlyThe 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_state is the state after your call.
  • version_no and content_hash describe the version your act appended. content_hash is the If-Match value your next amendment needs, so you do not have to read the document again first.
  • transition_fired is true for 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 answers transition_fired: false with operation: null. It is not an error. The state catches up when the neighbour stamps its exit.
  • header.additional_details is "" 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.

SituationResult
No key400 VAL-IDEM-001. Nothing reaches the declaration.
The same key and the same bodyThe first response is replayed. Nothing is recorded twice.
The same key while the first request is still running409 BUS-IDEM-002. Wait, then retry with the same key.
The same key with a different body422 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.

ActNeeds the write turn?
entryNo. Entry is never gated.
departIt waits until the country behind you has stamped its exit.
exitYes. Otherwise 403 AUTHZ-ETD-008.
dischargeThe destination country only.
CancelThe 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_details says 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. Call depart first.

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 409 BUS-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 from DEPARTED, when it is an inland office reached after a release.
  • A discharge before the destination has stamped entry is 409 BUS-ETD-009. Record the entry first.
  • Sending next_country_code on a discharge, an entry or a release is refused with VAL-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:

  1. The last connected country stamps its exit as usual. The state is IN_TRANSIT.
  2. ITS sets a deadline from the declaration's own authorization.transit_time_end.
  3. Any later stamp withdraws the deadline.
  4. 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.

A system closure is not evidence of arrival

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_no and nationality, or omit vehicle entirely.
  • 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 REGISTERED or DEPARTED, until any country stamps an exit. After that the transit is completed or discharged, never erased. A later attempt is 409 BUS-ETD-009.
  • The comment is optional, at most 500 characters.
  • Send Content-Type: application/json even with an empty body, or the call is refused with 415 ERR-023.
  • Send an Idempotency-Key, as for a stamp.

Checklist

  • Call depart between registering or arriving and exiting. An exit without it is refused.
  • Send an Idempotency-Key on 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-063 as a wait.
  • Send next_country_code on exit only.
  • Close a transit with discharge, never with exit.
  • Keep content_hash from the response if you will amend next.