9. Integrations
The API
Everything an integration needs is JSON over HTTPS on the company's own subdomain, authenticated with an API key created by an admin and shown once. Decimals are strings — JSON numbers are floats and quantities and money are not. The API reference lists every endpoint.
The design rule throughout: the API is the truth, and writes are conservative. Scripts create drafts that a human approves; machines report what they did; nothing outside the system can commit money or invent stock.
Custom data
Every part, workstation and work order carries an opaque block of JSON the system validates, stores and never interprets. It is for integrations: where a printed part's G-code lives, a printer's network address, a filament's density. Edit it on the entity's Custom data page or through the API, and keep the system agnostic about what is in it.
Three shapes
ETL from a supplier. A script takes a supplier's order export, resolves their part numbers through GET /api/sourcing, and posts a draft order with POST /api/purchase-orders. Unknown numbers fail the whole request naming the line; map them under sourcing and retry. After a few orders your sourcing table is the cross-reference.
A machine that makes things. A print-farm manager polls GET /api/workstations/{id}/next while its printer is idle, starts the step, prints, completes with the quantity (and, soon, the actual filament used), and sets the station idle. When it needs a human it goes blocked and pushes a task with PUT /api/tasks/{source}. The full design is in the repository under docs/integrations/print-manager.
Pulling data out. A sales system that needs quantities reads GET /api/stock; a dashboard elsewhere reads GET /api/parts. Read-only keys for read-only uses.
Being told
Webhooks are the system calling out when something happens: a work order completes, stock changes. An admin names a URL and the events it wants; each message is signed, retried if the receiver is down, and logged. The event is written in the same transaction as the change it reports, so nothing is announced that did not happen.
A system that asks for work and wants to hear when it is done. Mission control raises a service with POST /api/work-orders, giving the hull as the order's reference. When the last step is completed, the work_order.completed webhook carries that reference back, and the vessel can be returned to service. Foudra never learns what a hull is.
Polling still works for everything and is always correct; a webhook only removes the wait.
Next: rhythm.