Collections API
Create, browse, update, and delete collections. A collection is identified by :owner/:slug.
GET /api/collections
No auth required
Browse public collections with optional search.
Query parameters
q | Search by collection name |
limit | Max results (default 50, max 100) |
offset | Pagination offset |
Response 200
[
{
"id": "uuid",
"slug": "pubpub-archive",
"name": "PubPub Archive",
"description": "Full archive of PubPub publications",
"ownerSlug": "knowledge-futures",
"ownerName": "Knowledge Futures",
"latestVersion": "v3.2.0",
"createdAt": "2026-01-15T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z"
}
]POST /api/accounts/:owner/collections
Auth: write scope
Create a new collection under an account. You must own the account or be a member of the org.
Request
{
"slug": "my-dataset",
"name": "My Dataset",
"public": true
}Response 201
{
"id": "uuid",
"owner": "yourname",
"slug": "my-dataset",
"name": "My Dataset"
}GET /api/collections/:owner/:slug
No auth for public collections
Get collection metadata and latest version summary.
Response 200
{
"id": "uuid",
"slug": "pubpub-archive",
"name": "PubPub Archive",
"description": "Full archive of PubPub publications",
"public": true,
"ownerSlug": "knowledge-futures",
"ownerName": "Knowledge Futures",
"createdAt": "2026-01-15T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z",
"latestVersion": {
"semver": "v3.2.0",
"recordCount": 4521,
"fileCount": 892,
"totalBytes": 1073741824,
"metadata": { "description": "Full archive...", "readme": "..." },
"createdAt": "2026-04-01T00:00:00.000Z",
"message": "April sync"
}
}PATCH /api/collections/:owner/:slug
Auth: write scope
Update collection metadata. Pass only the fields to change.
Request
{
"name": "New Name",
"public": false
}Response 200
{"ok": true}DELETE /api/collections/:owner/:slug
Auth: write scope + owner/admin role in the owning org
Delete a collection and all its versions, records, and file references. Files themselves are not deleted (they may be referenced by other collections).
Response 200
{"ok": true}GET /api/accounts/:owner/collections
No auth required
List all collections belonging to an account. Non-owners see only public collections.
Response 200
[
{
"id": "uuid",
"slug": "pubpub-archive",
"name": "PubPub Archive",
"public": true,
"createdAt": "2026-01-15T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z"
}
]PATCH /api/collections/:owner/:slug/metadata
Auth: write scope
Update version metadata by creating a new patch version. The request body is a JSON object whose fields are merged with the previous version's metadata. Use this to update description, readme, license, or any other metadata fields without pushing new records.
Request
{
"description": "Updated description of the archive",
"readme": "# My Collection\nNew readme content.",
"license": "CC-BY-4.0"
}Fields
description | Short description of the collection. |
readme | Markdown readme content. |
license | License identifier (e.g. "CC-BY-4.0"). |
... | Any other key-value pairs. All fields are merged into the previous version's metadata object. |
Response 201
{
"semver": "v3.2.1",
"hash": "private:e5f6a7b8...",
"metadata": {
"description": "Updated description of the archive",
"readme": "# My Collection\nNew readme content.",
"license": "CC-BY-4.0"
}
}Large collections
A metadata edit creates a patch version, which means recomputing both version digests over the record set and copying every record-membership row. Past a few million records that takes longer than a proxy will hold the connection open. Add ?async=true to get an immediate 202 with a job_id, then poll for the outcome:
PATCH /api/collections/:owner/:slug/metadata?async=true
→ 202 { "job_id": "3f9c…", "status": "running", "base_semver": "v3.2.0" }
GET /api/collections/:owner/:slug/metadata/jobs/3f9c…
→ 200 { "job_id": "3f9c…", "status": "completed",
"result": { "semver": "v3.2.1", "hash": "private:e5f6a7b8…", "metadata": { … } } }The job's status is running, completed or failed. On completed, result holds exactly what the synchronous call would have returned; on failed, error holds the rejection. Only one metadata update runs per collection at a time.
Errors
409 | A metadata update is already in progress for this collection. The response carries its job_id. |
422 | No versions exist yet. Push a version first before updating metadata. |
POST /api/collections/:owner/:slug/fork
Auth: write scope
Fork a public collection into a target organization. Creates a new collection under the target org with the source's latest version. Records, schemas, and files are referenced (not copied); zero additional storage.
Only fully-public collections can be forked by a non-member. A fork copies the full record bodies by reference and gives the forker owner-level access to them, so if you are not a member of the source org and the source holds any private content — private records, private types, or private fields — the request is refused with 403 rather than leaked. Members of the source org can always fork.
Request
{
"targetOrg": "my-org",
"slug": "my-fork"
}Fields
targetOrg | Required. Slug of the organization to fork into. You must be a member of this org. |
slug | Optional slug for the new collection. Defaults to the source collection's slug. |
Response 201
{
"id": "uuid",
"owner": "my-org",
"slug": "my-fork",
"name": "PubPub Archive",
"forkedFrom": {
"owner": "knowledge-futures",
"slug": "pubpub-archive",
"version": "v3.2.0"
},
"version": {
"semver": "v1.0.0",
"recordCount": 4521
}
}Errors
403 | Not a member of the target org; or the source holds private content and you are not a member of the source org; or your API key is scoped to specific collections. |
404 | Source collection not found, not public, or target org not found. |
409 | A collection with the same slug already exists in the target org. |
422 | Source collection has no versions to fork. |