CDES Engine

API Documentation · v1.0

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 inInbound manifest out
cameroonWCO GOVCBR-103 XMLWCO GOVCBR-200 XML
beninASYCUDA World SAD XMLASYCUDA World Manifest XML
nigeriaB’Odogwu SGD JSONB’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

GET/formats🔒 API key

Returns the supported countries with their input (SGD) and output (manifest) formats.

Request
curl -H "X-API-Key: $CDES_API_KEY" http://localhost:8080/formats
Response · 200
[
  { "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

POST/translate🔒 API key

Parses the source country’s export declaration and renders the destination country’s inbound cargo manifest. The core operation.

Query parameters
NameTypeDescription
from requiredstringSource country (export SGD): cameroon · benin · nigeria
to requiredstringDestination country (inbound manifest)
Request — Benin export → Nigeria manifest
curl -H "X-API-Key: $CDES_API_KEY" -X POST \
  --data-binary @SAD-EXP.xml \
  "http://localhost:8080/translate?from=benin&to=nigeria"
Response · 200 · application/json — array of BOLs (abridged)
[ {
  "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": [ ]
} ]
Request — Nigeria export → Benin manifest
curl -H "X-API-Key: $CDES_API_KEY" -X POST \
  --data-binary @SGD_Export.json \
  "http://localhost:8080/translate?from=nigeria&to=benin"
Response · 200 · application/xml (abridged)
<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

POST/canonical🔒 API key

Parses an export declaration into the country-neutral canonical model — the hub the engine translates through. Useful for debugging what was extracted.

Query parameters
NameTypeDescription
from requiredstringSource country
Request
curl -H "X-API-Key: $CDES_API_KEY" -X POST \
  --data-binary @SGD_Export.json \
  "http://localhost:8080/canonical?from=nigeria"
Response · 200 · application/json (abridged)
{
  "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

POST/print🔒 API key

Renders a plain-text summary of the canonical model — a quick eyeball of a declaration.

Query parameters
NameTypeDescription
from requiredstringSource country
Request
curl -H "X-API-Key: $CDES_API_KEY" -X POST \
  --data-binary @GOVCBR-103.xml \
  "http://localhost:8080/print?from=cameroon"
Response · 200 · text/plain (abridged)
==================================================================
  CANONICAL SINGLE GOODS DECLARATION (SGD)
==================================================================
  Direction:               IMPORT
  Source:                  cameroon / WCO GOVCBR-103 XML
  Reference:               H0001-2018-DAU-001397-E
  ...

Health probe

GET/healthpublic

Public liveness probe for monitoring and load balancers — no key required.

Request
curl http://localhost:8080/health
Response · 200
{ "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.

POST/manifests🔒 country / admin

Transform the posted export SGD into the destination manifest, store both (unique by the declaration reference), and forward the manifest to the destination country.

Query parameters
NameTypeDescription
from requiredstringSource country (must match a country token, unless admin)
to requiredstringDestination country
Request
curl -H "X-API-Key: $BENIN_TOKEN" -X POST \
  --data-binary @SAD-EXP.xml \
  "http://localhost:8080/manifests?from=benin&to=nigeria"
Response · 201 Created
# 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.

GET/manifests🔒 country / admin

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.

Query parameters
NameTypeDescription
destinationstringAdmin only; ignored for country tokens
sourcestringFilter by source country
referencestringExact reference number
statusstringPENDING · DELIVERED · FAILED · NOT_CONFIGURED
includePayloadbooleanInclude the manifest body in each row (default false)
page, sizeintPagination (size ≤ 100)
Request
curl -H "X-API-Key: $NIGERIA_TOKEN" http://localhost:8080/manifests
Response · 200
{
  "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
}
GET/manifests/{reference}🔒 country / admin

Fetch one stored manifest by reference, with its payload — scoped to the caller's country.

Request
curl -H "X-API-Key: $NIGERIA_TOKEN" \
  http://localhost:8080/manifests/BJ-EXP-2026-000417
Response · 200
{ "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." }
StatusWhenExample error
401Missing/invalid key, or a token not permitted for the countryMissing or invalid API key…
409Reference already stored (duplicate submission)A manifest with reference '…' already exists…
404Unknown manifest referenceNo manifest found for reference '…'
400Missing from/to parameterRequired parameter 'to' is not present
400Empty request bodyEmpty request body; POST the national declaration
400Declaration has no reference numberThe declaration has no reference number…
400Unknown country / invalid XML or JSONNo declaration parser for country 'x'…

Quick reference

Method & pathPurposeAuth
GET /formatsSupported 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 /manifestsPull stored manifests (own country)🔒 country/admin
GET /manifests/{reference}Fetch one stored manifest🔒 country/admin
GET /healthLiveness probepublic
GET /v3/api-docsOpenAPI 3 contract🔒 key
GET /swagger-ui.htmlInteractive 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.