Errors and troubleshooting
Find what you see below, then follow the action. The error catalogue lists every code. When you ask for help, share the HTTP status, the error code and the correlation id. Never share your client secret, your private key or an access token.
What a refusal looks like
A refusal uses the same envelope as a success. The code is in header.additional_details:
{
"header": {
"response_code": 400,
"response_message": "Validation failed",
"additional_details": "Error code: ERR-015. CorrelationId: ... 2 validation error(s)",
"correlation_id": "8f1c9a2e-3b7d-4c11-9f8a-2d5e7b0c4a13"
},
"validation_errors": [
{ "field": "general_information.departure", "message": "must not be null" },
{ "field": "commodities", "message": "must not be empty" }
]
}
- Match on the code, never on the message. The code is stable. The message may change or be translated.
- A structural validation failure names every field at once.
validation_errors[].fieldis a dotted path intodata, such ascommodities[0].hs_code, so you can fix a payload in one round trip. - Log the correlation id. It is also in the
X-Correlation-IDresponse header. You can send your ownX-Correlation-IDon a request; ITS then records that value.
Refusals from the Idempotency-Key check happen before the request reaches ITS's own error handling, and use a shorter shape:
{
"code": "VAL-IDEM-001",
"error": "The Idempotency-Key header is required on this endpoint",
"category": "VALIDATION"
}
Code prefixes
| Prefix | Kind | Typical HTTP |
|---|---|---|
VAL- | Validation: the request is not acceptable as sent | 400 |
BUS- | Business rule: the request is valid but not allowed now | 409, sometimes 400 or 412 |
AUTHZ- | Authorisation: you may not do this, or not for this country | 403 |
RES- | Resource: not found, or not yours | 404 |
INT- | Integration: a deployment or partner-system problem, not your request | 502 or 503 |
SYS- | System: ITS could not complete the request | 500 or 503 |
ERR- | Platform: refused before any ITS rule ran | varies |
The codes you will meet most
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
ERR-015 | 400 | Structural validation failed | Read validation_errors and fix every field it names |
VAL-ETD-003 | 400 | A value is not in its reference vocabulary | Check the code against Reference data |
VAL-ETD-004 | 400 | The station does not exist or is in another country | You can only stamp at your own offices |
VAL-ETD-007 | 400 | Invalid route: same departure and destination, next_country_code missing on an exit or sent on another act, or a destination trying to exit | See Lifecycle and stamps |
VAL-ETD-034 | 400 | A listing filter, order-by or tz could not be read | The message names what was wrong |
VAL-ETD-064 | 400 | Not a stamping act | Use entry, depart, exit or discharge |
VAL-IDEM-001 | 400 | Idempotency-Key missing | Send one on every stamp, cancellation and acknowledgement |
VAL-IDEM-003 | 422 | The key was already used with a different body | Use a new key for a new act; reuse a key only for an identical retry |
BUS-IDEM-002 | 409 | A request with this key is still running | Wait, then retry with the same key |
AUTHZ-ETD-008 | 403 | Not your country's turn to act | See Who may act, and when |
AUTHZ-ETD-014 | 403 | Your credential cannot be attributed to an organisation | Contact the Secretariat |
AUTHZ-ETD-016 | 403 | You are not entitled to act for this country | Check which country you are acting for |
AUTHZ-ETD-017 | 403 | No submission arrangement is recorded for you | The Secretariat must record your participation or delegation |
AUTHZ-ETD-021 | 403 | Section F, the departure formalities, cannot be amended | Correct a wrong stamp with another stamp |
BUS-ETD-009 | 409 | Not valid in the declaration's current state | Read the declaration and act on its current state |
BUS-ETD-063 | 409 | The goods cannot be released yet: the previous country has not exited | A wait. The message names the country |
BUS-ETD-082 | 409 | Discharge stamped away from the declared office of destination | The message names the right office |
BUS-ETD-083 | 409 | Entry or exit stamped at an office registered as inland | Check the station, or correct your register |
VAL-ETD-018 | 428 | An amendment without If-Match | Send the content hash from the ETag |
BUS-ETD-019 | 412 | If-Match is stale: someone amended since you read | Read again, reapply your change and resubmit |
BUS-ETD-020 | 409 | The amendment changes nothing | Nothing to do |
VAL-ETD-028 | 400 | Amendment reason missing | Send envelope.amendment_reason |
BUS-ETD-022 | 409 | A concurrent change | Safe to retry |
RES-ETD-001 | 404 | No such declaration, or you may not see it | ITS will not say which |
RES-ETD-030 | 404 | No announcement of that version for your country | Check the version_no you acknowledge |
SYS-ETD-023 | 503 | ITS could not sign the version. Nothing was recorded. | Retry later |
Refused before ITS ran
These come from the HTTP layer, before anything specific to eTDs has run:
| Code | HTTP | Meaning |
|---|---|---|
ERR-002 | 404 | No such path. Different from an eTD 404, which means no such declaration or not yours. |
ERR-022 | 405 | The wrong method on a real path. The likeliest cause is an endpoint that moved. See Changes to the API. |
ERR-023 | 415 | Your Content-Type is not one this endpoint reads, or you sent a body with none. Send Content-Type: application/json. |
ERR-024 | 406 | Your Accept header asks for something this endpoint cannot produce. |
A 400 with an empty body usually means an unencoded > or < in a filter. Send them as %3E and %3C.
Look up a refusal afterwards
Every refusal is recorded for 90 days. Look it up by its correlation id:
curl --fail-with-body --silent --show-error \
--cert "$ITS_CLIENT_CERT" --key "$ITS_CLIENT_KEY" \
--header "Authorization: Bearer $ITS_TOKEN" \
"$ITS_GATEWAY_URL/etd/rejections?correlation_id=8f1c9a2e-3b7d-4c11-9f8a-2d5e7b0c4a13"
{
"header": { "response_code": 200, "response_message": "SUCCESS" },
"data": [
{
"correlation_id": "8f1c9a2e-3b7d-4c11-9f8a-2d5e7b0c4a13",
"received_at": "2026-08-15T22:41:07.221Z",
"method": "POST",
"path": "/api/v1/etd",
"status": 400,
"error_code": "ERR-015",
"reason": "INVALID_FIELDS",
"validation_errors": [
{
"field": "general_information.departure",
"message": "must not be blank",
"rejected_value": null
}
]
}
]
}
- Without
correlation_id,GET /etd/rejectionslists your organisation's refusals, newest first. It takespage-numberandpage-size(default 10, at most 100). pathis the route template, such as/api/v1/etd/{serial_no}, so you can group by it.reasonis a coarse group:INVALID_FIELDS,UNKNOWN_VALUE,BUSINESS_RULE,NOT_ENTITLED,NOT_FOUND,RATE_LIMITED,PRECONDITION,UNPARSEABLEorINTERNAL_ERROR.error_codeis the precise one.- Request bodies are not stored, only the fields and values ITS objected to. Your credentials are never stored.
The notifications feed also sends integration.rejections when ITS refused a number of your requests in a day.
Retry or fix?
| What you see | Retry? |
|---|---|
4xx | No. Fix the request first. The exceptions are BUS-ETD-022 and BUS-ETD-063, which are safe to retry later, and BUS-ETD-019, which needs a fresh read. |
5xx | Yes, with controlled backoff. Keep the same Idempotency-Key and the same body. |
| A timeout | Yes, with the same Idempotency-Key and body. The first attempt may have landed. |
| A TLS or certificate error | No. Investigate. Never turn off certificate checks. |
FAQ
Every call returns 403
Check that the Authorization header is actually sent. A call with no token is refused with 403 everywhere under /api/v1, not 401. If the header is there, check the error code: AUTHZ-ETD-017 means the Secretariat has not yet recorded who you may submit for.
My stamp was refused with BUS-ETD-063
The country behind you has not stamped its exit, so you cannot release the goods inland yet. The message names that country. Wait and retry. Your entry stamp was recorded.
My entry answered transition_fired: false
You stamped entry at a one-stop border post before the previous country stamped its exit. The entry is recorded. The state moves when your neighbour exits. It is not an error.
My amendment was refused with 412
Someone changed the declaration after you read it. Every stamp appends a version, so this happens during ordinary movement. Read it again, reapply your change and resubmit with the new ETag.
My amendment cleared fields I did not mean to change
An amendment replaces the whole document. A field you leave out of a section you send is cleared. Read the declaration, change what you need, and send all of it back.
A declaration I expect to see answers 404
Either it does not exist, or your country is not party to it: it did not file it, will not receive it, has not stamped it and has not been named next. ITS will not tell you which.
The list ignores my paging
cursor and limit were removed from GET /etd. An unknown parameter is ignored, so you get page one at the default size. Use page-number and page-size.
I get 405 on an endpoint that used to work
The endpoint moved. See Changes to the API.
My exit named a country with no system on ITS
The exit is recorded and additional_details says so. Nobody is told and nobody can read the declaration through that country. When a country on ITS lies further along the route, name that country on your exits instead.