Customs Engine
The server-side engine behind the Customs & Global Trade Operations add-on — a jurisdiction Strategy Pattern for tariff-classification forms, plus eleven AI reasoning endpoints that power the "Stage 2" half of every hybrid Embedding+AI screen in the add-on.
What Is the Customs Engine?
server/engines/CustomsEngine.ts is the single server-side file that backs every AI-calling and jurisdiction-aware screen in the Customs & Global Trade Operations add-on. It has two distinct jobs living in one file:
- A jurisdiction Strategy Pattern (its oldest part, mirroring Patent Agent's and Compliance Engine's shape) — given a country code and a schema category, it picks the matching
CustomsSchemaStrategy(Turkey/GTİP, United States/HTS, or a generic default) and returns a ready-to-render broker-authorization, Incoterms-rule, or tariff-classification form. - Eleven independent AI reasoning endpoints, one per screen in the add-on, each following the exact same shape: build a prompt from the request, route it to Cohere / Anthropic API / Claude CLI depending on
apiMode, parse the model's JSON response, and log the result via the Event & Analytics Engine.
In one sentence
Customs Engine is where every "Stage 2" AI call in Customs & Global Trade Operations actually runs — the frontend's job is only to find the right context via local embeddings first (Stage 1) and hand it to one of this engine's eleven endpoints.
The Two-Stage Hybrid Pattern This Engine Completes
Every AI-calling screen in Customs & Global Trade Operations runs the same two-stage flow, and Customs Engine is always Stage 2:
| Stage | Where it runs | What it does |
|---|---|---|
| Stage 1 | Entirely client-side, in src/services/customsEmbeddingService.ts, which wraps the shared Knowledge Engine | Vectorizes the screen's query text against the tenant's own live ledger (sanction rules, declarations, clearance records, freight benchmarks, and so on) via the user's own local Ollama instance (ollamaEngine.embed + cosine similarity), never touching the server. |
| Stage 2 | Client-side (callClaude()) or server-side (this engine), depending on the user's Settings → Agentic choice | Takes the top Stage-1 matches and reasons over them — risk scoring, overcharge/overpayment detection, discrepancy diagnosis, etc. Only reached on the server when writerEngine is 'api' or 'local-cli'. |
Why does Stage 1 never reach the server?
ollama/local-llm/web-llm all run in the user's own browser or on their own machine — the Render server has no network path to a user's 127.0.0.1:11434 Ollama instance. If a client function skipped this check and always called the server, selecting Ollama in Settings would silently produce confusing 500s instead of routing locally. This is documented project-wide as the "Full Writer Engine Support Rule," and every one of Customs Engine's eleven client-side counterparts implements it.
The candidate pool is always the tenant's own ledger
Unlike a few other engines in TotalApp that rank against a small static example corpus, every Customs screen's Stage-1 search ranks the tenant's own live records — there is no separate external regulation corpus (a real OFAC/EU/UN sanctions archive, a real government tariff schedule, a real carrier SLA database) wired in yet. "Search Similar Rules" on the Trade Compliance screen, for instance, semantically ranks the sanction rules already on file rather than a real government list.
Part 1 — Jurisdiction Schema Strategy
resolveCustomsStrategy(countryCode) upper-cases the incoming code and switches between three concrete CustomsSchemaStrategy classes, each implementing one method: getSchema(category).
| Strategy | Jurisdiction | Statute referenced |
|---|---|---|
| TRCustomsStrategy | Turkey (GTİP — 12-digit tariff code) | 6769 Sınai Mülkiyet Kanunu, TSE/CE/Tareks permit fields |
| USCustomsStrategy | United States (HTS — 10-digit tariff code) | FDA/EPA/FCC permit fields |
| DefaultCustomsStrategy | Any unmapped country | Generic fallback fields, never throws |
Each strategy branches again on category, returning one of three different field sets:
broker_agency
Customs registration number, Power of Attorney reference and expiry date, digital signature certificate number, and assigned customs offices — powers the Customs Brokers & Agencies screen's authorization modal.
incoterms_rules
Incoterm code, risk transfer point, buyer freight-cost share (%), buyer risk share (%), and (TR only) a VAT exemption code — powers the Incoterms & Delivery Rules screen.
Default (tariff classification)
HS/GTİP or HTS code, product description, duty rate (%), excise rate (%, TR only) and required permits/certifications — powers the HS Code Catalog screen.
The resulting UiComponent Form tree is rendered by the same <DynamicScreenRenderer> component every other server-driven-UI schema in TotalApp uses — served by handleCustomsEngineGenerateSchema at POST /api/customs-engine/generate-schema.
Part 2 — The Eleven AI Reasoning Endpoints
Every AI-calling screen in Customs & Global Trade Operations has its own dedicated handler function in this file. All eleven share one identical shape — build prompt, route by apiMode (Cohere command-r7b-12-2024 / Anthropic claude-sonnet-4-6 / Claude CLI), parse the model's JSON, log via Event & Analytics Engine — and differ only in their prompt content and output schema:
| Handler | Endpoint | Screen it powers | What it computes |
|---|---|---|---|
handleCustomsEngineTradeComplianceAudit | /customs-engine/trade-compliance-audit | Trade Compliance & Sanctions | Denied-party match, dual-use license requirement, military-use risk vector, and required documentation for a proposed export/shipment. |
handleCustomsEngineExportDeclarationAudit | /customs-engine/export-declaration-audit | Export Declarations | Missing invoice details, dual-use export flag, and currency valuation variance note for an outbound (ETGB) filing. |
handleCustomsEngineImportDeclarationAudit | /customs-engine/import-declaration-audit | Import Declarations | Undervaluation risk warning, reference price gap (%), and a TAREKS safety-inspection hold recommendation. |
handleCustomsEngineClearanceRiskAudit | /customs-engine/clearance-risk-audit | Clearance Status | Routing motive, predicted additional delay in hours, and whether a physical (Red Line) inspection is likely. |
handleCustomsEngineOriginDocValidationAudit | /customs-engine/origin-doc-validation-audit | Certificates & Origin Docs | Regional Value Content threshold check, non-originating material risk, and chamber-endorsement verification for a preferential origin document. |
handleCustomsEngineCarrierRateAudit | /customs-engine/carrier-rate-audit | Freight Rate Benchmark | Carrier overcharge detection, spot-rate arbitrage opportunity, and an estimated price leakage amount for a shipping lane. |
handleCustomsEngineDemurrageRiskAudit | /customs-engine/demurrage-risk-audit | Global Shipment Tracking | Predicted arrival variance, free-time expiration urgency, and a recommended container gate-out priority. |
handleCustomsEngineFreightBookingAudit | /customs-engine/freight-booking-audit | Carrier & Forwarder Hub | SLA contract-breach detection, booking allocation drop detection, and a hazardous-cargo handling accuracy estimate for a logistics provider. |
handleCustomsEngineLandedCostAudit | /customs-engine/landed-cost-audit | Landed Cost Calculator | Duty-spike detection, misallocated terminal-charge flag, and a sanity-check recalculated per-unit landed cost. |
handleCustomsEngineDutyLedgerAudit | /customs-engine/duty-ledger-audit | Customs Duty & Tariff Ledger | Overpayment detection, drawback (refund) eligibility, miscalculated tariff-bracket flag, and an estimated refund amount. |
handleCustomsEngineDocMatcherLinterAudit | /customs-engine/doc-matcher-linter-audit | Doc Matcher & Linter | Net weight gap, HS code mismatch, and currency discrepancy flags — via a two-pass Linter (raise candidates) → Critic (confirm/discard) prompt structure, folded into one call. |
Why eleven near-identical handlers instead of one generic one?
Each handler's prompt is domain-specific — a sanctions-screening prompt and a duty-drawback prompt ask the model to reason about completely different things and return a different JSON shape. Keeping them as separate, independently testable functions (rather than one parameterized mega-handler) means a change to one screen's reasoning logic can never silently affect another's, at the cost of some repeated routing boilerplate that is intentional, not accidental duplication.
How Server-Side Routing Works — apiMode
Every one of the eleven handlers applies the exact same routing logic once a request actually reaches the server (meaning writerEngine was 'api' or 'local-cli' on the client — Ollama/local-llm/web-llm never send a request here at all):
apiMode | Route | Model |
|---|---|---|
'cohere' | Cohere Chat API | command-r7b-12-2024 |
anything else, with ANTHROPIC_API_KEY set | Anthropic Messages API | claude-sonnet-4-6 |
| anything else, no API key configured | Local Claude CLI (spawn('cmd', ['/c','claude','--print']) on Windows) | Whatever the CLI session resolves to |
Every completed or failed call — regardless of which of the three paths served it — is logged via logEvent() to the Event & Analytics Engine with engineName: 'CustomsEngine', a per-handler workflowId (e.g. 'customs-engine-duty-ledger-audit'), and a duration measurement, so every screen's AI usage shows up in the same tenant-wide analytics history as every other engine in TotalApp.
Where the Customs Engine Fits
This engine has no persistence layer of its own — every screen's ledger data (sanction rules, declarations, clearance records, freight benchmarks, duty entries, and so on) lives in its own tenant-scoped JSON store under customs-trade/, managed by src/services/customsTradeService.ts, not by this engine. Customs Engine's job is strictly the reasoning step: given the context a screen already assembled (via its own Stage-1 embedding search), produce a structured judgment and hand it back.
On the client, every one of the eleven corresponding functions in customsTradeService.ts (runTradeComplianceAudit, runExportDeclarationAudit, runImportDeclarationAudit, runClearanceRiskAudit, runOriginDocValidationAudit, runCarrierRateAudit, runDemurrageRiskAudit, runFreightBookingAudit, runLandedCostAudit, runDutyLedgerAudit, runDocMatcherLinterAudit) reads writerEngine from useUIStore first, calls callClaude() directly for the three local engines, and only calls this server engine's matching endpoint for 'api'/'local-cli'.
A reasoning layer, not a data layer
Customs Engine never reads or writes a tenant's stored records directly — it receives exactly the context the calling screen assembled in its request body, reasons over it, and returns a result. Persisting that result back into the screen's own ledger is the calling screen's own responsibility, via customsTradeService.ts. In practice, screens differ on whether their row actions (as opposed to the AI diagnostic itself) persist anything — see the FAQ below.
Frequently Asked Questions
apiMode routing shape is add-on-specific, though; both patterns are the same ones Patent Agent, Compliance Engine and Matrix Agent already use elsewhere in TotalApp. A handler cannot simply be imported by another add-on, since each one's request/response shape and prompt are hand-written for its own screen's data model.submitDutyRefundClaim, transmitExportDeclaration, updateProviderSLATier) generate a receipt reference and persist a real status change on the record. Others (e.g. lockLandedCostAllocation, applyDocLinterFix, enforceTradeSanctionHold) only produce a toast notification with a receipt reference — the underlying record is untouched, and a page refresh reverts any visual "locked/fixed" state. Neither class is wired to a real external system (a government filing gateway, a bank, a chamber of commerce) yet; both are placeholders pending a future integration.CustomsSchemaStrategy (e.g. EUCustomsStrategy) and one new branch in resolveCustomsStrategy()'s conditional chain. No screen calling the schema endpoint needs to change — they already pass whatever countryCode the user's jurisdiction picker resolves to. Note this engine currently supports fewer jurisdictions (TR/US/Default) than Patent Agent's five (TR/US/WO/EP/Default) — EU and WIPO customs forms both fall back to the generic Default strategy today.