Skip to content

Customs declarations

Regimes

Dossier.Regime System Message XML envelope
IM — Import IDMS IE415 IE415B / IIE415B
EX — Export AES CC515 CC515C
T1 / T2 — Transit NCTS CC015C CC015C

The regime is set at upload time (manual) or via declaration_type (ingestion) and determines the entire downstream processing: AI extraction prompt, transport cost calculation target value, XML generator used, and declaration type options on the Declare page.


Declaration types

IM — Import

Field Options
DeclarationType IM
AdditionalDeclarationType A — Standard, D — Simplified, E — Simplified (warehouse), X — Pre-arrival
UseH2B = false (H1B) Standard import, procedure 40
UseH2B = true (H2B) Customs warehouse entry, procedure 71 — adds Warehouse element to XML

EX — Export

Field Options
DeclarationType EX
AdditionalDeclarationType A — Standard, C — Simplified (authorised location), D — Simplified (standard), F — Pre-lodged
Procedure codes 10 — Standard export, 21 — Re-export from warehouse, 23 — Temporary admission re-export

Dossier workflow

Manual path (file upload)

flowchart TD
    UP(["Upload + select regime"])
    UP -->|PDF — IM / EX| B["ProcessingWorker"]
    UP -->|PDF — T1 / T2| BT["ProcessingWorker"]
    UP -->|XLSX| XP["XlsxProcessor\ndirect parse"]

    subgraph imex["IM / EX extraction"]
        B --> ADI["Azure Document Intelligence\nOCR / layout"]
        ADI -->|OCR text| CL["Azure OpenAI\nclassify_document"]
        CL -->|invoice / packing_list| EX["Azure OpenAI\nextract_invoice / extract_packing_list"]
    end

    subgraph transit["Transit extraction"]
        BT --> TADI["Azure Document Intelligence\nOCR / layout"]
        TADI -->|OCR text| TEX["Azure OpenAI\nextract_transit"]
    end

    EX -->|status=Review| C["Review page\ncorrect extracted data"]
    TEX -->|status=Review| C
    XP -->|status=Review| C
    EX -->|status=Failed| F
    TEX -->|status=Failed| F

    C --> D["TransportCosts page\nadd freight / insurance"]
    D --> E["Declare page\nreferences & contacts"]
    E -->|IM| G1["IE415BXmlGenerator\nIE415"]
    E -->|EX| G2["CC515CXmlGenerator\nCC515C"]
    E -->|T1/T2| G3["CC015CXmlGenerator\nCC015C"]
    G1 & G2 & G3 --> H(["Download / submit\nto customs"])
    F(["Failed\nsee LastErrorMessage"]) -->|Retry| B
    F -->|Retry| BT

Ingestion path (API / ServiceBus)

flowchart TD
    API(["POST /api/declarations\n(X-Api-Key or Bearer)"])
    SB(["ServiceBus\ncanonical queue"])

    API --> IS["ICanonicalIngestionService"]
    SB --> IS

    IS --> RC{"Resolve client"}
    RC -->|ClientCode match| CL[(Existing client)]
    RC -->|EORI match| CL
    RC -->|no match| NC["Auto-create client\nIsVerified=false"]
    NC --> CL

    IS -->|declaration_type=IM| A1["CanonicalToImportAdapter\n→ CorrectedImportData"]
    IS -->|declaration_type=EX| A2["CanonicalToExportAdapter\n→ CorrectedExportData"]
    IS -->|declaration_type=T1/T2| A3["CanonicalToTransitAdapter\n→ CorrectedTransitData"]

    A1 & A2 & A3 --> D[(Dossier\nStatus=Draft\nSource=ApiIngestion\nor ServiceBusIngestion)]

Dossier statuses

stateDiagram-v2
    [*] --> Draft : upload or ingestion
    Draft --> Processing : queued for extraction
    Processing --> Review : AI extraction OK
    Processing --> Failed : AI / endpoint error
    Failed --> Draft : Retry
    Review --> Approved : Review saved
    Approved --> Submitted : XML generated
    Submitted --> [*]
Status Enum value Meaning
Draft 0 Just uploaded or ingested; not yet queued or processing
Queued 6 Waiting in the processing channel queue
Processing 1 AI extraction in progress
Review 2 Extraction complete; awaiting user review
Approved 3 Reviewed and approved; ready to declare
Submitted 4 XML generated and submitted to customs
Failed 5 AI extraction or endpoint error — see LastErrorMessage; use Retry button

Ingested dossiers always start as Draft (no AI extraction; data comes from the canonical payload). They enter the review queue alongside manually created dossiers.


Transport cost division

Applies to both IM and EX. Handled on the /Dossiers/{id}/TransportCosts page.

Regime Target value What to enter
IM CIF — cost + insurance + freight to EU border International freight (from origin to BE)
EX FOB — free on board at EU exit point Internal transport cost to EU border

Distribution is proportional by line value across all goods items. The incoterm group (E/F, C, DAP) determines whether costs are added or deducted and which formula applies:

  • E & F terms (EXW, FOB, FCA…): add AK (sender → EU border), add CA (EU border → destination), subtract FA (declaration cost)
  • C terms (CIF, CFR, CPT…): no AK, add CA, subtract FA
  • DAP / DDP / DPU: deduct BA (inside-EU portion), add CA, subtract FA

XML generation (IM / CC415)

Implementation note: in code this message is generated through the IE415B flow for IDMS import declarations.

Two message sub-types driven by CustomsDeclaration.UseH2B:

CC415_H1B CC415_H2B
Procedure 40 71
Trader Exporter Seller
Warehouse element
SupportingDocument 4006 ✅ (AGP licence)

HS code format

Regime Digits Split for XML
IM / EX 10 digits (no dots) 1–6 → harmonizedSystemSubheadingCode
7–8 → combinedNomenclatureCode
9–10 → taricCode
T1 / T2 8 digits (no dots) 1–6 → harmonizedSystemSubheadingCode
7–8 → combinedNomenclatureCode

Transit does not use taricCode.

Country resolution

Country names from AI extractions are resolved to ISO2 via the Tarbel GeographicalArea table. Unknown names (e.g. TURKIYE, USA) are resolved via the CountryAliases section in appsettings.json — see configuration.md.


XML generation (EX / CC515C)

Generated by CC515CXmlGenerator using CorrectedExportData. The SMF wrapper uses System=AES, Module=EXPORT, Identity=CC515C.

Key differences from CC415 (import): - Root element: CC515C within the standard Descartes SMF envelope - ExportOperation instead of ImportOperation - Exporter is mandatory with full address - ExitOffice (customs office of exit) — from CorrectedExportData.ExitCustomsOffice - Route: ordered list of transit country codes - No Warehouse element - AuthorisedLocation element with typeOfLocation / qualifier / UNLocode

CorrectedExportData fields are populated either by: - Manual path: user fills in review/declare pages (ReviewExport, DeclareExport) - Ingestion path: CanonicalToExportAdapter maps fields from the CanonicalDeclaration


XML generation (T1/T2 / CC015C) — groupage

CC015CXmlGenerator supports two operating modes selected automatically:

Mode Trigger Behaviour
Simple (PDF path) No TransitGoodsItem.HouseConsignmentSequence set Auto-increments house consignment numbers from 1; unchanged from original
Groupage (ingestion path) Any goods item has HouseConsignmentSequence set Uses Navision/Qargo sequence numbers as-is; emits referenceNumberUCR and Consignee per house consignment from CorrectedTransitData.HouseConsignments

This ensures Qargo T2 messages (where HC sequence numbers are Navision shipment line IDs, not sequential) produce correct XML without affecting the existing PDF extraction flow.


SMF schema validation

When Descartes wrapper validation is enabled (Descartes:Smf:ValidateXsd=true), generated SMF XML is validated against SMF.xsd before returning/submitting.

Required settings: - Descartes:Smf:ValidateXsd - Descartes:Smf:XsdFolder

In Docker images built from this repo, XSD files are bundled under /app/schemas/smf, and Docker defaults set: - Descartes__Smf__ValidateXsd=true - Descartes__Smf__XsdFolder=/app/schemas/smf