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>
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-Keyon 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 beforeaccept.
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_urlwith{ 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.