Every error — validation, auth, domain — is an
RFC 7807 problem document
(application/problem+json) whose detail is always one plain-language
sentence:
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}.
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
{"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.
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.