CruzLink

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.

Core tables
TableHolds
orgA shipper, carrier, port, broker, or the demo authority org.
app_userOne row per authenticated user.
role_grantServer-granted role per user per org. Users never write this table.
shipmentThe spine: reference, state, the pinned rule version, and the Common field set below.
documentPre-arrival documents: commercial invoice, packing list, bill of lading, each with a client-computed SHA-256.
custody_eventOne hash-chained row per pickup, handoff, arrival, inspection, or receipt.
milestonePayment-eligibility events. An event, never a transfer.
rule_versionVersioned, dated rate rules. SAMPLE until a Founder ruling marks one LIVE.
audit_logAppend-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.

shipment.common
FieldTypeMeaning
pricenumberDeclared commercial value.
freightnumberFreight charge.
insurancenumberInsurance charge.
currencystringISO 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.

The seam contract
TableFieldContract
adapter_registrykindace, caricom_single_window, or erp.
modesimulated or live. Every adapter is simulated until a Founder ruling flips it. Callers read the mode; they don't infer it from behavior.
adapter_payloaddirectionoutbound (what CruzLink sent) or inbound (what came back).
rawThe 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 →→→→
Role gates
TransitionRequires
filedshipper or broker_finance
under_review, cleared, or heldcustoms_reviewer
receivedport_operator or carrier
closedbroker_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.