METIS Public API playbook¶
This playbook covers /public/ — a fully open, unauthenticated, read-only JSON
projection of exactly the data already public on the site's /view/* pages. It
exists so external frontends (React apps, embeds, dashboards) can build their own
views over that data instead of scraping or iframing the HTML pages.
The live schema at /public/openapi.json and Swagger UI at /public/docs are
authoritative — this page is narrative and conventions, not a contract restatement.
Where it lives¶
The same /public/ paths are served from two hosts, and the responses are identical:
- the view host, alongside the public pages this API mirrors — that host serves those pages and this API and nothing else;
- the app host, alongside the rest of METIS.
Prefer the view host if you are building against the public data alone: it is the smallest surface, and the one whose URL shape follows the public pages.
Authentication¶
None. Every endpoint is open — there is no token, key, or cookie to send.
What "public" means here¶
Nothing is decided by a boolean flag on a record. Visibility is journey/step/field
config driven, the same rule the /view/* pages use:
- A Membership or Holon relationship is publishable when
public-visibleis set on the step it currently rests on, or on its whole journey. - A team membership counts when it sits at a step flagged
team-active— that flag grants edit permission on the website and is reused here as "an actual working member," never as a publishing rule of its own. - An info or link field is included only when its class configuration marks it
public_visible: true.
This surface and /view/* share the exact same filtering functions
(metis_apps/gathering/public_org_links.py, metis_apps/metis/public_holon_presentation.py)
so they cannot silently drift on what counts as public. If something is missing here
that you can see on the website, it's a bug in this API, not a different rule.
CORS¶
Access-Control-Allow-Origin: * — any origin may call this from browser JS. Only
GET and OPTIONS are allowed, and no credentials/cookies are ever sent or
accepted, so the wildcard is safe.
Endpoints¶
| Endpoint | What it returns |
|---|---|
GET /public/domain/ |
The domain/site home: the domain holon, its team, and every local gathering with its camps |
GET /public/orgs/{org_slug}/ |
An organisation's public info/links, team, and publicly-linked camps/gatherings |
GET /public/gatherings/{lg_slug}/ |
A local gathering: camps, team, publicly-linked orgs, and a programme teaser |
GET /public/gatherings/{lg_slug}/camps/{camp_slug}/ |
A camp: team, related orgs grouped by journey, public info fields, programme preview |
GET /public/gatherings/{lg_slug}/programme/ |
Aggregate public programme across a gathering and its camps (q, owner filters) |
GET /public/gatherings/{lg_slug}/camps/{camp_slug}/programme/ |
Full public programme for one camp |
GET /public/gatherings/{lg_slug}/experiences/{experience_slug}/ |
A gathering-owned Experience's detail page |
GET /public/gatherings/{lg_slug}/camps/{camp_slug}/experiences/{experience_slug}/ |
A camp-owned Experience's detail page |
GET /public/people/{person_id}/ |
A person's public page: publishable memberships only |
A 404 on a person/org/gathering/camp/experience endpoint means "no public record at
that address" — it does not distinguish "doesn't exist" from "exists but nothing on
it is public," matching /view/*'s behavior.
Not exposed here¶
- Share-hash person pages and their vCard
(
/view/share/<id>/<hash>/,/view/share/<id>/<hash>/vcard/) — these use a distinct, capability-URL access model (an HMAC-gated link, not "already public by default") and are intentionally not mirrored here. The vCard carries the full contact card, so it lives behind the same hash as the page offering it; there is no unhashed/view/person/<id>/vcard/.
Rate limiting¶
None for now. This is fully open with zero authentication, so it is more exposed to abuse than the other surfaces — but per this project's architecture posture, no new infrastructure (Redis, a gateway, etc.) is added without a measured problem. If abuse is observed, the reverse-proxy layer is the first lever, not application code.