Developer docs v0
CruzLink's data model has three tiers: a fixed Core schema every shipment shares, a typed Common field set for the values a landed-cost calculation needs, and an adapter-ownedUnique tier that stores whatever a partner or authority system actually sends, untouched.
v0. This describes the schema as it stands after Slice 7. It will grow with the API; nothing here is a stability promise yet.
Core
The shared transaction spine. Every table below exists regardless of which adapters are live.
| Table | Holds |
|---|---|
org | A shipper, carrier, port, broker, or the demo authority org. |
app_user | One row per authenticated user. |
role_grant | Server-granted role per user per org. Users never write this table. |
shipment | The spine: reference, state, the pinned rule version, and the Common field set below. |
document | Pre-arrival documents: commercial invoice, packing list, bill of lading, each with a client-computed SHA-256. |
custody_event | One hash-chained row per pickup, handoff, arrival, inspection, or receipt. |
milestone | Payment-eligibility events. An event, never a transfer. |
rule_version | Versioned, dated rate rules. SAMPLE until a Founder ruling marks one LIVE. |
audit_log | Append-only. Every state change and milestone write lands here. |
Common
Typed JSONB on shipment, fixed in Slice 5. This is what a landed-cost estimate actually reads.
| Field | Type | Meaning |
|---|---|---|
price | number | Declared commercial value. |
freight | number | Freight charge. |
insurance | number | Insurance charge. |
currency | string | ISO currency code for the three fields above. |
document.common exists in the schema for later document-level fields; it has no fixed shape yet, so nothing reads it as of v0.
Unique
The adapter seam. A partner or authority system's raw payload is stored exactly as received, in adapter_payload.raw, and mapped to Core/Common at read time rather than forced into a shared shape at write time.
| Table | Field | Contract |
|---|---|---|
adapter_registry | kind | ace, vibir_excise, caricom_single_window, orerp. |
mode | simulated or live. Every adapter is simulated until a Founder ruling flips it. Callers read the mode; they don't infer it from behavior. | |
adapter_payload | direction | outbound (what CruzLink sent) or inbound (what came back). |
raw | The untouched payload. Nothing is dropped or reshaped on the way in. |
Two adapters fire server-side the instant a shipment is filed, independently of each other, because they model two distinct real agencies: ace_simulated (U.S. Customs and Border Protection) and vibir_excise_simulated (the Virgin Islands Bureau of Internal Revenue's excise filing, Form 721VI, which travels alongside every real CBP submission). Each writes one outbound row shaped like that agency's own filing, one inbound row shaped like a synchronous acknowledgment, both carrying "simulated": true in their own payload in addition to the registry's mode. Going live means changing that one adapter'smode and pointing its trigger at a real endpoint; nothing that readsadapter_payload has to change shape, and the two adapters go live independently of each other.
Shipment state machine
The only sanctioned way to change a shipment's state is the advance_shipment(shipment, next_state) function. Every transition is gated by role and written to the audit log.
draft → filed → under_review → cleared → received → closed
↓ ↑
held →→→→| Transition | Requires |
|---|---|
→ filed | shipper or broker_finance |
→ under_review, cleared, or held | customs_reviewer |
→ received | port_operator or carrier |
→ closed | broker_finance |
Reaching received creates a payment_eligible milestone automatically. That is the only place money-related state changes; nothing in CruzLink moves funds.
Custody events
Each row carries kind (pickup, handoff,arrival, inspection, or receipt), lat/lng, device_id, an optional photo_path, and prev_hash /hash. hash is computed from the row's own fields plus the previous event's hash, set by trigger; clients never supply either hash column.
Questions about access to a sandbox or the beta API: use support.