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, caricom_single_window, or erp. |
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. |
The first adapter, ace_simulated, fires server-side the instant a shipment is filed: one outbound row shaped like an ACE entry summary, 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 row'smode and pointing the trigger at a real endpoint; nothing that readsadapter_payload has to change shape.
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.