Configuration
Primary defaults live in appsettings.json. For local development, override with appsettings.Development.json and user secrets or environment variables. For production (IIS or container), prefer environment variables and keep secrets out of repo.
Connection strings
"ConnectionStrings": {
"DefaultConnection": "Server=sql-host,1433;Database=customstuf;User Id=app;Password=***;TrustServerCertificate=true",
"TarbelConnection": "Server=sql-host,1433;Database=tarbel;User Id=app;Password=***;TrustServerCertificate=true"
}
The Tarbel database is managed by the application. Admins upload the ZIP from the minfin portal via /Admin/Tarbel; the TarbelImportWorker imports GoodsNomenclature, GeographicalArea, MeasureType, AdditionalCode, Certificate, Measure, and related tables into the Tarbel database configured by TarbelConnection.
Auth
Set Auth:Provider to select the authentication backend:
| Value | Backend |
|---|---|
EntraId |
Microsoft Entra ID (Azure AD) |
| (empty) | Treated as Entra ID |
Entra ID
"Auth": {
"Provider": "EntraId",
"EntraId": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "",
"ClientId": "",
"ClientSecret": "",
"CallbackPath": "/signin-oidc",
"CertificateThumbprint": "" // optional — use cert instead of secret
}
}
Only Entra ID authentication is supported by the current code.
AI provider
Set AI:Provider to either AzureOpenAI or OpenAI.
AzureOpenAI: Azure endpoint + deploymentOpenAI: OpenAI API key and optional OpenAI-compatibleBaseUrl- If
AI:Provideris empty, the app auto-detects provider from configured keys
"AI": {
"Provider": "AzureOpenAI",
"AzureOpenAI": {
"Endpoint": "https://your-resource.openai.azure.com/",
"DeploymentName": "gpt-4o",
"UseKeylessAuth": true, // true = app registration (ClientSecretCredential)
"ApiKey": "" // set if UseKeylessAuth = false
},
"OpenAI": {
"BaseUrl": "",
"ApiKey": "",
"Model": "gpt-4o-mini"
}
}
When UseKeylessAuth is true, the client authenticates via ClientSecretCredential using the shared Azure:* credentials block (see below). When false, ApiKey is required.
Azure credentials (shared)
Both Azure OpenAI and Azure Document Intelligence use the same app registration when UseKeylessAuth is true:
Azure Document Intelligence
Used for native PDF OCR/layout analysis before sending text to Azure OpenAI. Optional — if not configured, the app falls back to GPT vision (PDF rasterised to JPEG).
"DocumentIntelligence": {
"Endpoint": "https://your-resource.cognitiveservices.azure.com/",
"UseKeylessAuth": true, // uses Azure:* credentials block above
"ApiKey": "", // set if UseKeylessAuth = false
"ModelId": "prebuilt-layout"
}
| Key | Default | Description |
|---|---|---|
Endpoint |
— | Required. Azure Document Intelligence resource endpoint URL |
UseKeylessAuth |
true |
When true, uses shared Azure:* ClientSecretCredential |
ApiKey |
— | Required when UseKeylessAuth is false |
ModelId |
prebuilt-layout |
Document model to use for analysis |
Storage
UploadFolder is where uploaded PDFs/XLSX files and temp upload files are stored.
Branding
Branding is configured from appsettings.json (not from Admin settings):
"Branding": {
"TitlePlain": "CustomsTUF",
"TitleHtml": "<span class=\"brand-prefix\">Customs</span><span class=\"brand-suffix\">TUF</span>",
"MainColor": "#119E50"
}
TitlePlain: plain-text title (e.g. login page and metadata)TitleHtml: rendered in the main layout headerMainColor: global accent color
Descartes SMF XSD validation
SMF schema validation is optional and controlled by config:
For Docker images built from this repo:
- schemas/smf/*.xsd are bundled into publish output
- runtime defaults are set via env vars in Dockerfile:
- Descartes__Smf__ValidateXsd=true
- Descartes__Smf__XsdFolder=/app/schemas/smf
Processing
PdfDpi controls the resolution at which PDFs are rasterised before being sent to the AI vision model. 300 DPI is recommended — lower values cause digit misreads.
Country aliases
The AI sometimes returns country names that differ from the Tarbel database entries (e.g. TURKIYE instead of TURKEY, USA instead of UNITED STATES). Add aliases here without a rebuild:
Keys are case-insensitive. Values must be valid ISO 3166-1 alpha-2 codes. When the AI returns a name not in the Tarbel GeographicalArea table, the resolution order is:
- Already a 2-letter ISO2 code → pass through
CountryAliaseslookup (this section)- Exact match on
DescriptionENorDescriptionNLin Tarbel - Partial / starts-with match in Tarbel
- Log warning, leave as-is
Declarations (company-wide defaults)
Stored in the AppSettings table (editable via /Admin/DeclarationFields), not in appsettings.json. Prefix: declarations:.
| Key | Description |
|---|---|
declarations:sender_gln |
GLN of the sender (MessageHeader) |
declarations:declarant_eori |
Declarant / representative EORI |
declarations:declarant_name |
Declarant name as it appears in XML declarations |
declarations:declarant_street |
Declarant street and house number |
declarations:declarant_city |
Declarant city |
declarations:declarant_postcode |
Declarant postcode (also issuingPlaceCode) |
declarations:crin |
Declarant CRIN reference number (Descartes SMF) |
declarations:auth_c501 |
Authorisation C501 reference (AEO-C) |
declarations:auth_c505 |
Authorisation C505 reference (guarantee) |
declarations:auth_c517 |
Authorisation C517 reference (AGP) |
declarations:auth_c506 |
Authorisation C506 reference (REM) |
declarations:supervising_customs_office |
Default supervising customs office ref |
declarations:warehouse |
Default warehouse identifier (H2B) |
declarations:location_of_goods |
Default UN/LOCODE for location of goods |
declarations:type_part_three |
TypePartThree code in SMF declarations (e.g. T2LF) |
declarations:contact_phone |
Contact phone number for declarations |
declarations:contact_name |
Contact person name for declarations |
declarations:contact_email |
Contact person email for declarations |
Tarbel settings
Stored in the AppSettings table (editable via /Admin/Tarbel).
| Key | Default | Description |
|---|---|---|
tarbel:auto_download |
false |
When true, automatically downloads and imports any pending Tarbel extractions during the daily sync cycle (runs at 02:00 UTC). When false, the worker still refreshes the latest available extraction key each night (so the admin banner stays current) but does not download. The download can always be triggered manually via the Download & import button in /Admin/Tarbel. |
UN/LOCODE settings
Stored in the AppSettings table (editable via /Admin/Locode).
| Key | Default | Description |
|---|---|---|
locode:auto_sync |
false |
When true, automatically checks and re-imports UN/LOCODE data when a new version is published. |
locode:datapackage_url |
(datahub.io URL) | URL of the datapackage.json used to check for a new version. |
locode:csv_url |
(datahub.io URL) | Direct URL of the code-list.csv file to download. |