Files
0pt/docs/04-api.md
T
adminandClaude Opus 4.8 22e09d984d feat: rebrand MONO->0pt (hero, header, layout) + add project docs/contract
Hero is now a data-native 0pt artifact (no photos, keeps grain/reveal/float); header is the 0pt wordmark with Swedish nav; layout is sv + loads JetBrains Mono + 0pt metadata; globals wires the loaded fonts. Adds CLAUDE.md + docs/01-09 as the build contract. next build green (5 routes).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 17:38:07 +02:00

4.5 KiB

04 · API contract (FastAPI)

Canonical REST contract. Reconcile the existing 25+ endpoints in src/orchestrator/main.py toward this. JSON in/out, snake_case. All amounts in öre. Auth: Next.js → FastAPI over a shared internal token (X-Internal-Token); customer identity passed as org_id/user_id resolved from the Auth.js session server-side.

Conventions

  • Errors: { "error": { "code", "message" } }, proper HTTP status. Interface-voice messages (Swedish) are produced in the frontend, not here.
  • Idempotency: accept Idempotency-Key on POSTs that create orders/payments.
  • Pagination: ?limit=&cursor=.

Catalog

GET  /catalog                      -> [{id,title,markets,entity_type,count,updated,price_ore,confidence}]
GET  /catalog/{id}                 -> full dataset (overview, coverage, schema, quality summary)
GET  /catalog/{id}/sample?n=10     -> sample rows WITH per-field provenance

Estimate (configurator → price/time/confidence)

POST /estimates
  body: { flow, markets[], entity_type, fields[], enrichment[], volume, frequency, confidence_target, formats[] }
  -> { total_ore, deposit_ore, balance_ore, est_days, est_confidence, breakdown[] }

AI-assist (NL → config)

POST /ai/parse
  body: { text }                   -- "5000 svenska SaaS-bolag med e-post och omsättning"
  -> { config: <estimate body>, rationale }   -- prefilled configurator; uses pgvector match to catalog/fields

Orders (lifecycle)

POST /orders                       body: { flow, config }            -> { order_id, status:'draft'|'quoted' }
GET  /orders                       -> [orders for org]
GET  /orders/{id}                  -> order + current job + delivery (if any)
POST /orders/{id}/scope            (custom flow) body:{ notes }      -> status:'quoted' (manual quote step)
POST /orders/{id}/accept           customer approves delivery        -> triggers balance release; status:'accepted'
POST /orders/{id}/revise           body:{ notes }                    -> status:'revision'; escrow untouched

Payments (50/50 escrow)

POST /payments/{order_id}/deposit  -> { checkout_url }   (Stripe Checkout for 50%; on success → IN_PRODUCTION)
POST /payments/{order_id}/release  -> captures/charges the held balance (called on accept)
POST /webhooks/stripe              (Stripe → here) verify signature; update payments + order status

Implement balance as either a manual capture on a held PaymentIntent, or a second Checkout/invoice keyed to order_id. Never charge balance before accept.

Jobs / live status

GET  /orders/{id}/status           -> { stage, pct, per_market[], confidence, eta, rows }
GET  /orders/{id}/events           -> Server-Sent Events stream (preferred for the production tracker)

Uploads (R2 presigned)

POST /uploads                      body:{ order_id, name, mime, bytes } -> { file_id, put_url }  (client PUTs to R2)
GET  /orders/{id}/files            -> [files]

Customer DB access (read-only grant)

POST /connections/db-grant
  body: { order_id, conn_string, scope, expires_at }   -- stored ENCRYPTED; validated read-only
  -> { grant_id, status:'pending'|'verified' }
DELETE /connections/db-grant/{id}  -> revoke

Deliveries

GET  /orders/{id}/delivery         -> { formats, destinations, record_count, avg_confidence, quality_report, files[], provenance_export_url }
POST /orders/{id}/delivery/destinations  body:{ destinations[] }    -> updates targets (CSV/Parquet/JSON/API/NocoDB/Metabase/FiveCRM/Sheets/webhook)

Sources (the transparency moat)

GET  /sources                      -> [{source_id,name,market,access,licence,cadence,confidence}]
GET  /sources/{market}             -> sources + freshness for a market

Subscriptions / feeds

POST /subscriptions                body:{ order_id, cadence, webhook_url } -> { sub_id }
GET  /subscriptions                -> [subs]
POST /subscriptions/{id}/pause | /resume

Webhooks out (to customers)

  • On delivery / feed update: POST to subscriptions.webhook_url with { order_id, delta_url, summary }.

Health

GET  /health                       -> { ok, db, workers, queue }

Existing-code reconciliation

Map current endpoints (orders, estimates, ai-assistent, catalog, webhooks, health) onto the names above; rename for consistency, keep behavior. Add: /orders/{id}/events (SSE), /payments/* escrow split, /connections/db-grant, /sources, /subscriptions. Remove any endpoint that drives a bypass/stealth fetch path.