Skip to content

Errors & endpoint reference

Every error — validation, auth, domain — is an RFC 7807 problem document (application/problem+json) whose detail is always one plain-language sentence:

{"type": "about:blank", "title": "Validation Error", "status": 422,
"detail": "Invalid request: body.uri: Field required.",
"errors": [{"field": "body.uri", "message": "Field required", "type": "missing"}]}

Render detail to humans; branch on status and errors[].field in code.

Status When Notes
400 a value is unusable malformed uri, non-IRI theme, unknown expand path — detail says what to send instead
401 missing credentials no X-API-Key; on /keys, no bearer token
403 wrong credentials invalid/revoked key, or a key not authorized for the requested namespace
404 unknown resource entity, capsule, or namespace (namespaces are never lazily created)
422 body missing/mistypes a field errors[] lists every offending field by name

The one exception is deliberate: POST /capsules accepts with 202 and reports validation problems asynchronously in the capsule’s status — poll GET /capsules/{id}.

Header Required on Value
X-API-Key every data endpoint your API key (minted in the console under API → Settings)
X-Namespace every data endpoint except /namespaces target namespace slug — explicit, no default

Key management (/keys) is used by the console and authenticates with your Common Approach account (Firebase bearer token), not an API key.

Method Path Notes
POST /namespaces {"namespace", "label?"} → 201; idempotent
GET /namespaces list this tenant’s namespaces
DELETE /namespaces/{ns}?confirm={ns} drops all data in the namespace; confirmation required → 200 with graphs_dropped count
Method Path Notes
POST /orgs {"uri", "name", "description?"} → 201
GET /orgs list
GET /orgs/{id} add ?expand= for a tree read
GET /orgs/{id}/everything full theory-of-change tree
GET /orgs/{id}/export?format=jsonld|nquads that organization’s subgraph
Method Path Notes
POST /orgs/{org}/outcomes {"uri", "name", "description?", "themes?": [IRI]} → 201
GET /orgs/{org}/outcomes · /orgs/{org}/outcomes/{id}
PUT /orgs/{org}/outcomes/{id} properties only — no uri; identity never changes
DELETE /orgs/{org}/outcomes/{id} → 204
Method Path Notes
POST /orgs/{org}/indicators {"uri", "name", "description?", "unit?", "outcome_id?"} → 201 — set outcome_id so reports roll up to themes
GET /orgs/{org}/indicators · /orgs/{org}/indicators/{id}
PUT /orgs/{org}/indicators/{id} properties only — no uri
DELETE /orgs/{org}/indicators/{id} → 204
Method Path Notes
POST /indicators/{id}/reports {"uri", "value", "unit?", "period_start", "period_end"} → 201; same uri again replaces the value
GET /indicators/{id}/reports every report carries value (decimal or null) + value_text (verbatim string)
DELETE /indicators/{id}/reports/{report_id} → 204
Method Path Notes
POST /capsules raw JSON-LD body (≤ 6 MB; larger via /capsules/uploads) → 202 {capsule_id, status}; Idempotency-Key header for safe retries; ?replaces={id} to supersede an accepted capsule
POST /capsules/uploads {"filename"} → 201 {capsule_id, status, upload: {url, fields}, expires_in}; POST the file to upload.url as a multipart form (fields first, file last) and the ingest starts on landing
GET /capsules · /capsules/{id} list / status + plain-language validation report
GET /capsules/{id}/file {url, filename, size_bytes, expires_in} — download of the original bytes as submitted
GET /capsules/{id}/export?format=jsonld|nquads that capsule’s triples only — export
POST /capsules/{id}/copy {"namespace"} → 202; the bridge between a graph namespace and the account’s ~explorer file space, either way, same id on both sides
DELETE /capsules/{id} atomic rollback of everything that capsule asserted → 204
Method Path Notes
GET /rows/indicator-reports filters: theme, fund, org, period[from], period[to]; cursor pagination
GET /rows/organizations · /rows/outcomes · /rows/indicators · /rows/funding-statuses dimension tables, same envelope; org filter where it applies
GET /rows/{table}/export the whole table as one file, versioned (If-None-Match → 304) — snapshot export
GET /facets/themes · /facets/funds facets with counts — filter-picker feeds (/rows/themes, /rows/funds still work)
GET /export?format=jsonld|nquads whole namespace as CIDS — answers {url, expires_in, content_type, size_bytes}; fetch url within 15 minutes (details)
Method Path Notes
POST /jobs {"kind": "export", "params": {"orgs": [...], "period": {"from", "to"}, "format": "jsonld|nquads", "template?": "free text"}} → 202 {job_id, status}; omit orgs for every organization; 429 with Retry-After when the namespace already has its allowance of jobs running (two by default)
GET /jobs · /jobs/{id} list (newest first) / status (queued, running, succeeded, failed) with a 15-minute download url once succeeded; the file itself is kept permanently
GET /jobs/{id}/download 307 to a fresh download URL; 409 until the job has succeeded

Methodologies built on the console’s Common Framework pages, and the results of applying them, stored whole per tenant. Like /namespaces, these take only X-API-Key — a methodology refers to indicators by IRI and belongs to the tenant, not to one namespace. Documents are cf-methodology/1 and cf-run/1, capped at 1 MB; PUT replaces the whole document, last writer wins.

Method Path Notes
GET /methodologies summaries, most recently updated first
PUT /methodologies/{id} store the whole methodology document → 201 (new) or 200 (replaced)
GET /methodologies/{id} the document
DELETE /methodologies/{id} the methodology and its runs → 204
GET /methodologies/{id}/runs run summaries, newest first
POST /methodologies/{id}/runs store a run result → 201 or 200 by its id
GET /methodologies/{id}/runs/{run_id} the full result with every contribution
DELETE /methodologies/{id}/runs/{run_id} → 204

Aggregation itself runs in the browser today; the API is the store, and a server-side run over a namespace is planned.

Method Path Notes
GET /healthz unauthenticated liveness check
GET /docs interactive OpenAPI documentation
  • Organizations cannot be updated or deleted via CRUD — outcomes and indicators support PUT and DELETE; organizations do not yet. Capsule rollback removes capsule-sourced org data as normal.
  • Entity identity is the exact IRI — the same organization under two IRIs is two organizations; agree on one IRI per entity across intake paths (details).
  • Fund identity is exact-string matching on sff:forFunder.
  • A capsule never overwrites another. Two accepted capsules asserting the same IRI both stay, and table reads pick one. Use ?replaces= for corrections (capsules).
  • Namespace export of CRUD-created reports uses Lab’s compact vocabulary rather than the full i72:Measure structure; capsule-ingested reports export in official CIDS form.
  • Aggregation is client-side for now — rows are denormalized to make that trivial; server-side rollups are on the roadmap.