Skip to main content

Authentication

Every call your system makes to ITS carries two proofs:

ProofWhat it showsHow you send it
An OAuth 2.0 access tokenWhich client is callingAuthorization: Bearer <access_token> on every call
A client certificateThat the connection comes from your registered systemMutual TLS on every call to the gateway

Your connection may also be limited to the source addresses you registered. A call from another address is refused at the edge.

What you have

Keys, certificates and receiving endpoints are issued in the ITS portal's System integration workspace once the Secretariat admits your organisation.

ItemWhere it comes from
Client IDGenerated in the workspace. One confidential client per organisation per environment.
Client secretShown once, when you generate or rotate the credential.
Client certificate and private keyYour own. You register the certificate in the workspace.
Token URLThe Environments page.
Gateway base URLThe Environments page.
note

A step-by-step onboarding guide for the workspace is coming.

The examples in these guides read everything from shell variables, never from literal values:

VariableHolds
ITS_TOKEN_URLThe token URL for your environment
ITS_GATEWAY_URLThe gateway URL your System integration workspace shows for the environment, ending in /api/v1. It is the gateway host on the Environments page followed by /api/v1.
ITS_CLIENT_IDYour client ID
ITS_CLIENT_SECRETYour client secret
ITS_CLIENT_CERTThe path to your client certificate file
ITS_CLIENT_KEYThe path to your certificate's private key file
ITS_TOKENThe access token you got from the token URL

Paths in these guides, such as /etd/{serial_no}, are relative to the gateway base URL.

Get a token

Send a client credentials request to the token URL. The client ID and secret go in HTTP Basic authentication.

curl --fail-with-body --silent --show-error \
--user "$ITS_CLIENT_ID:$ITS_CLIENT_SECRET" \
--data "grant_type=client_credentials" \
"$ITS_TOKEN_URL"

A successful response is HTTP 200:

{
"access_token": "<access_token>",
"expires_in": 300,
"token_type": "Bearer"
}

The values above are examples. Always use the expires_in your response carries.

  • Keep the token in memory and reuse it. Do not request a token for every call.
  • Renew it about 60 seconds before it expires.
  • There is no refresh token. To renew, repeat the same request.

Call the gateway

Send the token and present your certificate on every call:

curl --fail-with-body --silent --show-error \
--cert "$ITS_CLIENT_CERT" --key "$ITS_CLIENT_KEY" \
--header "Authorization: Bearer $ITS_TOKEN" \
--header "Accept: application/json" \
"$ITS_GATEWAY_URL/etd?page-number=1&page-size=1"

This reads one page of the declarations you may see. It is safe to run before any declaration exists, and in Sandbox it is your first certification milestone.

Every response is wrapped in an envelope with a header and data:

{
"header": {
"operation": "GET",
"request_uri": "/api/v1/etd",
"response_code": 200,
"response_message": "SUCCESS",
"additional_details": "",
"correlation_id": "d4d599fa-..."
},
"data": [],
"pagination": {
"page_number": 1,
"page_size": 1,
"total_pages": 0,
"total_records": 0
}
}

Keep the correlation_id. It is how you look up a refusal afterwards. See Errors and troubleshooting.

Who ITS thinks you are

ITS resolves your organisation from the credential's binding, which the Secretariat controls. It never reads a country from a token claim or from your request. There is nothing to set, and sending a country claim changes nothing.

From that binding ITS works out two sets of countries:

  • the countries whose declarations you may see;
  • the countries you may act for — lodge, stamp, amend, cancel.
CallerSeesActs for
State PartyDeclarations its country filed, will receive, has stamped, or has been named next forIts own country, once the Secretariat records its participation
RECWhat any of its members may seeOnly the members that delegated submission to it
AfCFTA SecretariatEvery declarationNobody

GET /etd/scope tells you what your credential may see and act for. It describes the entitlement and confers none: ITS checks every call on its own.

A permission and an entitlement are different checks. A permission says what kind of act your client may perform. An entitlement says on whose behalf. A REC can hold the permission to stamp and still be refused a stamp for a country that has not delegated to it.

Permissions by endpoint

Endpoints are deny by default. Each needs a permission your client holds.

PermissionEndpoints
etd:issuePOST /etd
etd:readEvery read of a declaration, the list, /etd/reconciliation/*, /etd/rejections, attachment list and download, the PDFs
etd:endorsePOST /etd/{serial_no}/endorsements/{entry|depart|exit|discharge}
etd:amendPOST /etd/{serial_no}/versions
etd:cancelPOST /etd/{serial_no}/cancel
etd:attachPOST /etd/{serial_no}/attachments
etd:acknowledgePOST /etd/{serial_no}/deliveries/ack
notification:readGET /notifications and its sub-paths
notification:writePOST /notifications/{sequence_no}/read and POST /notifications/read-all

GET /etd/scope and reference data need a valid token and no permission. See Reference data.

When you are refused

A call with no token is not refused the same way everywhere:

PathAnswer to a call with no token
/api/reference/...401
Everything under the gateway base URL, such as /etd403

Both mean you were not authenticated. Do not branch on 401 alone. On either:

  1. Get a new token.
  2. Retry the call once.
  3. If it is refused again, check that the Authorization header is actually being sent before you look at permissions.

Three other refusals are common while you set up:

CodeMeaningWhat to do
AUTHZ-ETD-014Your credential cannot be attributed to an organisation.Contact the Secretariat. The credential is not bound.
AUTHZ-ETD-017No submission arrangement is recorded for your organisation.The Secretariat must record your participation or delegation first.
AUTHZ-ETD-016An arrangement exists and does not cover this country.Check which country you are acting for.

A TLS or certificate error is a connection problem, not an authorisation one. Never turn off certificate checks, and never use cURL's -k option.

Protect your credentials

  • Load the client secret and private key from a protected secret manager or protected environment variables.
  • Send the client secret only to the token URL. Never send it to the gateway.
  • Treat the access token as sensitive too. Do not log it.
  • Rotate a credential in the workspace if it may have been exposed. Rotation is self-service.

After a rotation, update your secret store, get a new token with the new credential and make one call to check it works.

Do not rotate credentials automatically when the token URL or the gateway returns a 5xx error. Those errors are temporary: retry with backoff instead.