Functions reference
Every HTTP endpoint the app exposes — 193 endpoints across 35 routers —
grouped by domain. For request/response schemas and a live "try it" console, use
the auto-generated interactive docs at /docs (Swagger) / /openapi.json.
Conventions
- Most endpoints require a session cookie and are scoped to the active workspace (tenant_id); sign in via POST /auth/login.
- Magic-link / token endpoints (/portal/{token}, /supplier/respond/{token}, /q/{token}, /invites/{token}) are intentionally unauthenticated — the token is the credential.
- /webhooks/* are verified by provider signature/handshake, not a session.
- Each page in Screens lists the endpoints it calls.
Auth & access (26)
Authentication — auth_routes (/auth)
| Method | Path | What |
|---|---|---|
| POST | /auth/register |
Create a user + first workspace |
| POST | /auth/login |
Password sign-in (may return a 2FA challenge) |
| POST | /auth/logout |
End the session |
| GET | /auth/me |
Current user + active workspace |
| POST | /auth/switch-tenant |
Switch active workspace |
| POST | /auth/forgot |
Request a reset link (always 200 — no account enumeration) |
| POST | /auth/reset |
Set a new password from the emailed token |
| POST | /auth/send-verification |
Send an email-verification link |
| POST | /auth/verify-email |
Confirm an email address |
| POST | /auth/mfa/enroll · /confirm · /disable |
Enrol / confirm / turn off TOTP 2FA |
| POST | /auth/mfa/verify |
Login step two: exchange a password-stage challenge + TOTP/recovery code for a session |
SSO — oidc (/auth/oidc)
| Method | Path | What |
|---|---|---|
| GET / PUT | /auth/oidc/{tenant_id}/config |
Read / set a workspace's OIDC config |
| GET | /auth/oidc/{tenant_id}/login · /callback |
Start / complete an OIDC sign-in |
Members & invites — members (/members), invites (/invites)
| Method | Path | What |
|---|---|---|
| GET | /members |
List workspace members + roles |
| POST | /members |
Add an existing user to the workspace |
| GET / POST | /members/invites |
List / create email invitations |
| POST | /members/invites/{iid}/revoke |
Revoke a pending invite |
| PATCH / DELETE | /members/{mid} |
Change a member's role / remove them |
| GET | /invites/{token} |
Preview an invitation (unauthenticated) |
| POST | /invites/{token}/accept |
Accept an invitation |
Sales — quotes & BOQ (28)
Quotes, lines & BOQ — quote_lines
| Method | Path | What |
|---|---|---|
| GET | /quotes |
All quotes, newest first (Quotes page) |
| GET | /quotes/{id} |
Quote header + lines (send/accept view) |
| GET | /quotes/{id}/boq |
Rolled-up Section→Group→Line BOQ with sub-totals |
| GET | /quotes/{id}/boq.pdf |
Server-rendered branded BOQ PDF (weasyprint) |
| POST | /quotes/import-boq |
Ingest a reference-BOQ fixture → grouped draft quote |
| POST | /quotes/extract-boq |
Raw rows / CSV → classify → grouped draft quote |
| POST | /quotes/extract-boq-pdf |
Drop a BOQ PDF → Gemini transcribe → classify → quote |
| GET / POST | /quotes/{id}/lines |
List / add lines |
| PATCH / DELETE | /quotes/{id}/lines/{line} |
Edit / remove a line (while draft) |
| PATCH | /quotes/{id}/lines/{line}/classify |
Human-verify a line's trade (learned per workspace) |
| POST | /quotes/{id}/lines/reorder |
Reorder lines |
| POST | /quotes/{id}/lines/{line}/choose |
Pick one option within a tier/alternative group |
| POST | /quotes/{id}/recommend-lines |
Match a raw materials list → SKUs + suggested costs |
Quote lifecycle — erp_routes, quotes
| Method | Path | What |
|---|---|---|
| POST | /quotes |
Create a quote |
| POST | /quotes/{id}/send |
Send to client (freezes the lines) |
| POST | /quotes/{id}/accept |
Mark accepted → creates a SOW |
| POST | /plans/{plan_id}/quote |
Build a quote from a solved plan's BOM |
| GET | /plans/{plan_id}/quote.pdf |
Quote PDF for a plan |
| POST | /quotes/{id}/send-for-signing |
Push the quote PDF to PandaDoc as a draft, then send |
Templates & share links — quote_templates, quote_share
| Method | Path | What |
|---|---|---|
| GET / POST | /quote-templates |
List / create reusable line bundles |
| GET / DELETE | /quote-templates/{id} |
Get / delete a template |
| POST | /quotes/{id}/apply-template |
Apply a template to a quote |
| POST | /plans/{plan_id}/share |
Issue a public quote share link |
| GET | /q/{token} |
View a shared quote (unauthenticated) |
Sales — SOW, billing & variations (13)
SOWs — erp_routes
| Method | Path | What |
|---|---|---|
| GET | /sows |
All SOWs (SOW & milestones page) |
| GET | /sows/{id} |
SOW header + milestones + scheduled invoices + variations |
| POST | /sows/{id}/generate-pos |
Draft POs for the job, grouped by vendor |
| POST | /billing/schedule-milestones |
Build the milestone schedule |
Billing & payments — billing
| Method | Path | What |
|---|---|---|
| POST | /sows/{id}/invoices/schedule |
Draft client invoices from the milestone schedule |
| POST | /invoices/{id}/push |
Push an invoice to the accounting provider (Xero/ERPNext) |
| POST | /invoices/{id}/mark-sent |
Mark an invoice sent |
| POST | /invoices/{id}/payments |
Record a client payment |
| POST | /sows/{id}/retention/release |
Release a retention amount |
| POST | /xero/reconcile-payments |
Pull recent Xero payments, mirror unseen ones locally |
Variations — variation
| Method | Path | What |
|---|---|---|
| POST | /sows/{id}/variations |
Raise a variation order |
| POST | /variations/{id}/approve |
Approve a variation |
| POST | /variations/{id}/invoice |
Invoice an approved variation |
Design pipeline — plans & geometry (38)
The sketch/scan → plan → BOM pipeline (absorbs the sketch2plan prototype).
Projects & ingest — projects (/projects), ingest
| Method | Path | What |
|---|---|---|
| GET / POST | /projects |
List / create projects |
| GET | /projects/{id} |
Project detail (contract value, costs) |
| POST | /projects/{id}/plans |
Create a plan under a project |
| POST | /projects/{id}/ingest/dxf · /pdf · /photo · /disto |
Ingest a CAD/photo/measurement source → a plan |
Plans, walls, openings, rooms, surfaces, fixtures — plans (/plans), walls, openings, rooms, surfaces, fixtures
| Method | Path | What |
|---|---|---|
| GET | /plans/{id} |
Plan state |
| POST | /plans/{id}/solve |
Run the chain-solver geometry |
| POST | /plans/{id}/confirm-dimensions |
Accept user-edited OCR dimensions, re-solve |
| POST | /plans/{id}/detect-walls · /confirm-walls |
VLM interior-wall detection / persist edits |
| POST | /plans/{id}/detect-doors · /confirm-doors |
Door detection / persist as openings |
| POST | /plans/{id}/detect-fixtures · /confirm-fixtures |
Fixture detection / persist edits |
| POST | /plans/{id}/extract-specs · /apply-specs |
Finishes-schedule VLM extract / apply SKU choices |
| POST | /plans/{id}/disto |
Ingest Leica Disto measurements (CSV/JSON) |
| GET / POST | /plans/{id}/scene |
Get / store the Floorplanner scene JSON |
| GET / POST | /plans/{id}/walls, PATCH/DELETE /walls/{id} |
List / create / edit / delete walls |
| GET / POST | /plans/{id}/openings, PATCH /openings/{id} |
Manage door/window openings |
| POST / GET | /plans/{id}/detect-rooms · /rooms |
Detect / list rooms |
| POST | /plans/{id}/decompose-surfaces · /decompose-by-room |
Build floor/ceiling/wall/skirting surfaces |
| GET / PATCH | /plans/{id}/surfaces, /surfaces/{id} |
List / tag surfaces |
| GET / POST | /plans/{id}/fixtures, PATCH /fixtures/{id} |
Manage fixtures |
Catalog & pricing (14)
Catalog — catalog (/catalog)
| Method | Path | What |
|---|---|---|
| GET / POST | /catalog/categories |
List / create material categories |
| GET / POST | /catalog/vendors |
List / create vendors |
| GET | /catalog/skus |
List SKUs (optionally by category kind) |
| GET | /catalog/skus/search |
Bilingual (中文 ⇄ EN) type-ahead over name/code/aliases |
| GET | /catalog/skus/{id}/cost-suggestion |
Recommended cost from price history (median + p25/p75 + latest) |
| POST / PATCH | /catalog/skus · /catalog/skus/{id} |
Create / edit a SKU (cost, aliases, reorder thresholds, material type) |
Pricing intelligence — pricing (/pricing)
| Method | Path | What |
|---|---|---|
| GET | /pricing/sku/{id}/suggest |
Cost suggestion for a SKU |
| GET | /pricing/sku/{id}/compare |
Compare a SKU's prices across observations/vendors |
| GET | /pricing/vendor/{id}/scorecard |
Vendor price/lead-time scorecard |
| GET | /pricing/projects/{id}/actuals |
A project's actual costs (committed POs / bills) |
| POST | /pricing/anomaly-check |
Flag prices that look out of band |
Procurement & RFQ (19)
Purchase orders, GRN & bills — procurement
| Method | Path | What |
|---|---|---|
| GET | /pos |
All purchase orders (procurement page) |
| GET | /pos/{id} |
PO header + lines + goods receipts + vendor bills |
| POST | /pos/{id}/send |
Issue a draft PO to the vendor |
| POST | /pos/{id}/grn |
Record a goods receipt |
| POST | /pos/{id}/bills |
Capture a vendor bill |
| POST | /bills/{id}/match |
Run the 3-way match (PO ↔ GRN ↔ bill) |
| POST | /bills/{id}/payments |
Record a vendor payment |
RFQ & supplier portal — rfq
| Method | Path | What |
|---|---|---|
| GET | /rfqs |
All RFQs |
| GET | /rfq/{id} |
RFQ header + lines + received quotes + invites |
| POST | /rfq |
Create an RFQ |
| POST | /rfq/{id}/invite · /send |
Invite a vendor (magic link) / send the RFQ |
| GET | /rfq/{id}/compare |
Side-by-side supplier comparison matrix |
| POST | /supplier-quotes/{id}/accept |
Accept a supplier quote → drafts a PO |
| GET / POST | /supplier/respond/{token} |
Supplier views / submits a quote (no account) |
| POST | /supplier/signup · /login · /account/history |
Optional persistent supplier login + history |
Warehouse & work-orders (20)
Warehouse — warehouse (/warehouse)
| Method | Path | What |
|---|---|---|
| GET | /warehouse/levels |
On-hand / reserved / available per item × location |
| GET | /warehouse/low-stock |
Items at/below their reorder point |
| GET | /warehouse/dashboard |
Inventory dashboard aggregate (value by material type & location, low/negative, currency) |
| GET / POST | /warehouse/locations, PATCH /{id} |
Manage stock locations |
| POST | /warehouse/events |
Append a stock event (receive/consume/adjust/transfer…) |
Work orders — workorders (/work-orders)
| Method | Path | What |
|---|---|---|
| GET / POST | /work-orders |
List / create fabrication jobs |
| GET | /work-orders/{id} |
Work-order detail + cutting-list BOM |
| POST | /work-orders/{id}/status |
Advance the stage (cutting→…→delivered) |
| POST | /work-orders/{id}/bom |
Add a BOM/cutting-list line |
| POST | /work-orders/{id}/seed-from-quote |
Seed the cutting list from a quote |
| POST | /work-orders/{id}/reserve · /consume |
Reserve / consume materials against the ledger |
Approvals & intake — approvals (/approvals), intake_warehouse
| Method | Path | What |
|---|---|---|
| GET | /approvals |
Pending draft-and-approve items |
| POST | /approvals/{id}/approve · /reject |
Execute / discard a draft |
| POST | /approvals/reorder |
Open a reorder-PO draft for the human queue |
| POST | /intake/warehouse |
Webform/agent intake → normalised stock/job effect |
CRM & client portal (8)
CRM (Attio) — crm
| Method | Path | What |
|---|---|---|
| POST | /attio/deals/{id}/sync |
Sync an Attio deal |
| POST | /projects/{id}/attio/stage · /note |
Push a deal stage / append a note |
| POST | /deal-registrations |
Register a partner-channel deal |
| POST | /deal-registrations/{id}/won |
Mark a registered deal won |
Client portal — client_portal
| Method | Path | What |
|---|---|---|
| POST | /projects/{id}/client-portal/issue |
Issue a client magic link for a project |
| GET | /portal/{token} |
Client views the quote (no account) |
| POST | /portal/{token}/accept-quote |
Client approves the quote |
Imports, integrations & webhooks (17)
Historical import — historical_import (/imports)
| Method | Path | What |
|---|---|---|
| GET / POST | /imports |
List / upload a historical quote/bill (CSV/Excel/PDF) |
| GET | /imports/{id} · /candidates |
Import status / fuzzy SKU-match candidates |
| POST | /imports/candidates/{id}/resolve |
Resolve a match (Link / New SKU) → price observation |
Xero — integrations_xero (/integrations/xero)
| Method | Path | What |
|---|---|---|
| GET | /integrations/xero/authorize · /callback |
OAuth connect flow |
| GET | /integrations/xero/items · /contacts |
Read Xero items / contacts (dry-run) |
Inbound webhooks — webhooks (/webhooks)
| Method | Path | What |
|---|---|---|
| GET / POST | /webhooks/whatsapp |
Meta handshake / inbound WhatsApp message |
| POST | /webhooks/attio |
Attio deal events in |
| POST | /webhooks/typeform |
Typeform intake in |
| POST | /webhooks/pandadoc |
PandaDoc e-sign status in |
| POST | /webhooks/xero |
Xero events → trigger payment reconcile |
| POST | /webhooks/chatwoot |
Shop-floor/customer message → classify → warehouse/work-order effect |
| POST | /webhooks/{provider} |
Generic verified webhook spine |
Admin & ops (10)
| Method | Path | What | Router |
|---|---|---|---|
| GET | /admin/margin-tracker |
Margin per quote/project | admin |
| POST | /admin/margin-rules |
Set margin rules | admin |
| POST | /admin/sku-overrides |
Per-SKU cost overrides | admin |
| POST | /projects/{id}/designer |
Assign a designer to a project | admin |
| POST | /admin/users/{id}/channels |
Set a user's notification channels | admin |
| GET | /admin/outbox |
Outbound integration-outbox browser (by provider + status) | admin |
| POST | /admin/outbox/{id}/retry |
Reset a failed/stuck outbox row to pending | admin |
| POST | /admin/webhooks/{event_id}/replay |
Replay a received webhook event | admin |
| GET | /forecast/pipeline |
Weighted sales pipeline + expected margin | forecast |
| GET | /healthz |
Liveness probe (unauthenticated) | health |
Generated from the route definitions in backend/api/ on 2026-06-14 (193 endpoints).
The live /docs is always authoritative for exact schemas.