Overview
The CDES Engine translates customs declarations across the Cameroon–Benin–Nigeria corridor on the “your export is my manifest” principle: you POST a country’s export declaration (SGD) and receive the destination country’s inbound cargo manifest, translated through a shared canonical model.
Country (from/to) | Export SGD in | Inbound manifest out |
|---|---|---|
cameroon | WCO GOVCBR-103 XML | WCO GOVCBR-200 XML |
benin | ASYCUDA World SAD XML | ASYCUDA World Manifest XML |
nigeria | B’Odogwu SGD JSON | B’Odogwu Manifest JSON |
Base URL
All paths are relative to your deployment’s origin:
# behind nginx / TLS (recommended) https://your-domain # direct http://your-server:8080
Examples below use http://localhost:8080.
Authentication
Every API endpoint requires an API key sent in the X-API-Key request
header. The one-page UI and this documentation are public; the /health
probe is public.
Send your key on every request
# store the key once in your shell export CDES_API_KEY="your-issued-key" curl -H "X-API-Key: $CDES_API_KEY" http://localhost:8080/formats
Missing or invalid keys return 401 Unauthorized. In the Swagger console,
open /swagger-ui.html?apiKey=<key> once, then click
Authorize and paste the key.
Formats & request bodies
For POST endpoints the body is the raw declaration —
XML for Cameroon/Benin, JSON for Nigeria. It is read directly from the request stream,
so any Content-Type is accepted (application/xml,
application/json, text/plain, or */*). Send the
declaration verbatim — do not form-encode or wrap it.
Response type: application/json for a Nigeria target,
application/xml for a Cameroon/Benin target.
List formats
Returns the supported countries with their input (SGD) and output (manifest) formats.
curl -H "X-API-Key: $CDES_API_KEY" http://localhost:8080/formats
[
{ "country": "cameroon", "sgdFormat": "WCO GOVCBR-103 XML", "manifestFormat": "WCO GOVCBR-200 XML" },
{ "country": "benin", "sgdFormat": "ASYCUDA World SAD XML", "manifestFormat": "ASYCUDA World Manifest XML" },
{ "country": "nigeria", "sgdFormat": "B'Odogwu SGD JSON", "manifestFormat": "B'Odogwu Manifest JSON" }
]
Translate to manifest
Parses the source country’s export declaration and renders the destination country’s inbound cargo manifest. The core operation.
| Name | Type | Description |
|---|---|---|
from required | string | Source country (export SGD): cameroon · benin · nigeria |
to required | string | Destination country (inbound manifest) |
curl -H "X-API-Key: $CDES_API_KEY" -X POST \ --data-binary @SAD-EXP.xml \ "http://localhost:8080/translate?from=benin&to=nigeria"
[ {
"identificationSegment": { "registryNumber": "BJ-EXP-2026-000417", … },
"bolSpecificSegment": {
"bolNature": "23", "bolIndicator": "BODOGWU",
"totalGrossMassManifested": 25370.7,
"bolTypeSegment": { "code": "House" },
"exporterSegment": { "name": "SOCIETE DE TRANSFORMATION INDUSTRIELLE DU BENIN" },
"consigneeSegment": { "name": "DELIFROST CATERERS NIGERIA LTD" }
},
"containers": [ ],
"items": [ { "itemNumber": 1, "hsCode": "04032090", "grossWeight": 25370.7 } ],
"vehicles": [ ]
} ]
curl -H "X-API-Key: $CDES_API_KEY" -X POST \ --data-binary @SGD_Export.json \ "http://localhost:8080/translate?from=nigeria&to=benin"
<AsycudaWorld_Manifest>
<Identification_segment>
<Voyage_number>E 7 2026</Voyage_number>
</Identification_segment>
<General_segment><Totals_segment>
<Total_number_of_packages>2240</Total_number_of_packages>
<Total_number_of_containers>19</Total_number_of_containers>
</Totals_segment></General_segment>
</AsycudaWorld_Manifest>
Canonical model
Parses an export declaration into the country-neutral canonical model — the hub the engine translates through. Useful for debugging what was extracted.
| Name | Type | Description |
|---|---|---|
from required | string | Source country |
curl -H "X-API-Key: $CDES_API_KEY" -X POST \ --data-binary @SGD_Export.json \ "http://localhost:8080/canonical?from=nigeria"
{
"sourceCountry": "nigeria",
"kind": "EXPORT",
"referenceNumber": "E 7 2026",
"exporter": { … }, "importer": { … },
"transport": { … }, "financial": { … },
"items": [ … ], "containers": [ … ]
}
See the field-mapping tables for every canonical field and its national source.
Human-readable report
Renders a plain-text summary of the canonical model — a quick eyeball of a declaration.
| Name | Type | Description |
|---|---|---|
from required | string | Source country |
curl -H "X-API-Key: $CDES_API_KEY" -X POST \ --data-binary @GOVCBR-103.xml \ "http://localhost:8080/print?from=cameroon"
================================================================== CANONICAL SINGLE GOODS DECLARATION (SGD) ================================================================== Direction: IMPORT Source: cameroon / WCO GOVCBR-103 XML Reference: H0001-2018-DAU-001397-E ...
Health probe
Public liveness probe for monitoring and load balancers — no key required.
curl http://localhost:8080/health
{ "status": "UP", "service": "cdes-engine" }
Manifest store & forwarding
The production flow: submit an export declaration, and the engine stores it, keys it by a unique reference, and forwards the resulting manifest to the destination country — which can then pull it.
Per-country tokens
These endpoints authenticate with X-API-Key too, but accept
a per-country token as well as the admin key. A country token acts only
for its own country (submit its exports, pull its inbound manifests); the admin key
(CDES_API_KEY) acts for any country.
Transform the posted export SGD into the destination manifest, store both (unique by the declaration reference), and forward the manifest to the destination country.
| Name | Type | Description |
|---|---|---|
from required | string | Source country (must match a country token, unless admin) |
to required | string | Destination country |
curl -H "X-API-Key: $BENIN_TOKEN" -X POST \ --data-binary @SAD-EXP.xml \ "http://localhost:8080/manifests?from=benin&to=nigeria"
# body = the destination manifest; plus response headers: X-CDES-Reference: BJ-EXP-2026-000417 X-CDES-Forward-Status: PENDING # PENDING | DELIVERED | FAILED | NOT_CONFIGURED Location: /manifests/BJ-EXP-2026-000417
409 Conflict if the reference already exists; 400 if the
declaration carries no reference number.
List stored manifests, scoped to the caller's country. A country
token returns only manifests destined for it; the admin key may pass
?destination= or omit it to see all.
| Name | Type | Description |
|---|---|---|
destination | string | Admin only; ignored for country tokens |
source | string | Filter by source country |
reference | string | Exact reference number |
status | string | PENDING · DELIVERED · FAILED · NOT_CONFIGURED |
includePayload | boolean | Include the manifest body in each row (default false) |
page, size | int | Pagination (size ≤ 100) |
curl -H "X-API-Key: $NIGERIA_TOKEN" http://localhost:8080/manifests
{
"content": [ {
"reference": "BJ-EXP-2026-000417", "source": "benin", "destination": "nigeria",
"manifestFormat": "B'Odogwu Manifest JSON",
"forwardStatus": "DELIVERED", "forwardHttpStatus": 200,
"forwardedAt": "2026-09-06T...", "manifest": null
} ],
"page": 0, "size": 20, "totalElements": 1, "totalPages": 1
}
Fetch one stored manifest by reference, with its payload — scoped to the caller's country.
curl -H "X-API-Key: $NIGERIA_TOKEN" \
http://localhost:8080/manifests/BJ-EXP-2026-000417
{ "reference": "BJ-EXP-2026-000417", "destination": "nigeria",
"forwardStatus": "DELIVERED", "manifest": "{ …the B'Odogwu manifest… }" }
404 if unknown; 401 if the manifest belongs to another country.
Errors
Errors return the matching HTTP status and a small JSON body:
{ "error": "Missing or invalid API key. Supply it in the 'X-API-Key' header." }
| Status | When | Example error |
|---|---|---|
401 | Missing/invalid key, or a token not permitted for the country | Missing or invalid API key… |
409 | Reference already stored (duplicate submission) | A manifest with reference '…' already exists… |
404 | Unknown manifest reference | No manifest found for reference '…' |
400 | Missing from/to parameter | Required parameter 'to' is not present |
400 | Empty request body | Empty request body; POST the national declaration |
400 | Declaration has no reference number | The declaration has no reference number… |
400 | Unknown country / invalid XML or JSON | No declaration parser for country 'x'… |
Quick reference
| Method & path | Purpose | Auth |
|---|---|---|
GET /formats | Supported countries & formats | 🔒 key |
POST /translate?from=&to= | Export SGD → inbound manifest | 🔒 key |
POST /canonical?from= | Export SGD → canonical model | 🔒 key |
POST /print?from= | Export SGD → text report | 🔒 key |
POST /manifests?from=&to= | Submit: store + forward the manifest | 🔒 country/admin |
GET /manifests | Pull stored manifests (own country) | 🔒 country/admin |
GET /manifests/{reference} | Fetch one stored manifest | 🔒 country/admin |
GET /health | Liveness probe | public |
GET /v3/api-docs | OpenAPI 3 contract | 🔒 key |
GET /swagger-ui.html | Interactive console | 🔒 key |
Machine-readable contract
Import the OpenAPI 3 spec into Postman or Insomnia from
/v3/api-docs (send your X-API-Key header), or use the bundled
docs/openapi.json.