Repository navigation
API Reference
The Hub's API is plain REST over /api/*, session-cookie authenticated, with the permission checks server-side on every call. This page is the orientation — conventions, auth, error shapes — with the surface organised by domain rather than an exhaustive per-endpoint dump. When in doubt, the handler files in src/endpoints/ are the definitive spec: one file per domain, the paths at the top of each.
-
Auth: session cookie from
/api/auth/login. MCP and scripts use a bearerHUB_TOKENissued against a Hub account — the same session, presented differently. -
Reads:
GET, list endpoints paginate;GET /:idor/:slugfor one. -
Writes:
POSTfor transitions, and transitions are named verbs —/present,/close,/withdraw,/review,/approve. A state change that isn't a named verb is usually a smell. -
Errors: JSON
{ error, detail? }; 401 unauthenticated, 403 the access engine declined, 422 validation, 429 rate-limited (the login limiter especially). - Idempotency on transitions that matter: ballots, writebacks, credential issuance carry their own dedupe — re-POSTing is safe-by-design, not by luck.
Auth & session
POST /api/auth/register /login /logout /forgot-password /reset-password
GET /api/auth/me /change-password
Membership & the directory
GET /api/directory /api/people /api/organisations
POST /api/member/affiliations /decide org membership requests
GET /api/member/workspace/:wg + /join /onboard
Decisions, elections, selections — the full pipeline from Governance and decisions:
GET|POST /api/decisions /:id /present /comments /flags
/flags/:flagId/respond /flags/:flagId/withdraw
/close /ballots /vetoes /vetoes/:vetoId/confirm
/withdraw /events
GET|POST /api/governance/elections/:id /advance /candidates
/candidates/:cid/screen /credential /vote /tally
/api/governance/selections/:id /open /apply /committee
/recuse /applications/:aid/evaluate /decide /announce
Platform bodies & Council
GET /api/platform/bodies /overview /public /records /tasks /enquiries
/api/council /api/council/:slug
POST /api/platform/bodies/:id/join /publish /withdraw-publication
POST /api/platform/decisions/:id/transition /contributions /publish
POST /api/platform/records/:id/revoke
Content, opportunities, resources
GET|POST /api/member/content /drafts /drafts/:id
GET /api/documents/:slug /resources /submissions /submissions/:slug
GET /api/opportunities (reviewed pipeline — see Content and publishing)
GET /api/events /events/:slug /coys /coys/:slug /gys /feed /search
Staff & team operations
/api/member/team/membership/… verify, status, terminate, records/:rid/end,
appeals/:id/review, renewals/run, handovers
/api/member/team/partnerships /recognition /coi /safeguarding /privacy
/api/member/admin/accounts/:id role, status, team-role, verify, reset-link
/api/member/admin/audit /notifications/send|outbox|preview
Negotiations
GET /api/negotiations /:slug POST /:slug/follow
GET|POST /api/negotiations/projects /:id /versions /amendments
/amendments/:id/reconciliations /:reconciliationId/confirm
POST /api/negotiations/documents/:id/versions/:versionId/extraction-reviews
Intelligence — read-only by design, one narrow writeback:
POST /api/intelligence/query GET /metrics
POST /api/intelligence/writebacks /:id/approve /:id/apply
Notifications & push
GET|POST /api/notifications/… POST /api/push/subscribe /unsubscribe /send
GET /api/push/vapid-key /status POST /api/cron/notifications-drain
Casting a decision ballot — note it is a named transition with a server-side eligibility recheck, not a PATCH to a votes field:
curl -X POST "$HUB/api/decisions/42/ballots" \
-H "Cookie: payload-token=$TOKEN" \
-H "Content-Type: application/json" \
-d '{"choice": "support"}'And the same call the MCP makes, as JSON-RPC over its own service (see Agent tools):
{"jsonrpc":"2.0","id":3,"method":"tools/call",
"params":{"name":"list_opportunities","arguments":{"status":"open"}}}No generic CRUD passthrough to collections. No /api/admin/* that bypasses the access engine. No way to write to authority_records except through the service layer behind member/admin and team endpoints. If you find yourself wanting "just a raw update endpoint", that want is the thing to code-review — the missing API is usually the security boundary.