Collections API
Create, browse, update, transfer, fork and delete collections. A collection is identified by :owner/:slug.
GET /api/collections
No auth required (mine=true needs a session or an unscoped key)
Browse public collections, or with mine=true the collections of every organization you belong to, public or not. facets count owners and tags across every collection in that set, not just this page and whatever the other filters. featuredTags and featuredCollections are the site’s picks for the explore page.
Query parameters
q | Collections whose name contains this text |
owner | Only collections of this account (its slug) |
tag | Only collections with this tag |
sort | name or records; by default, most recently updated first |
limit | Max results (default 50, max 100) |
offset | Pagination offset |
mine | true for your organizations’ collections. Anonymous callers and collection-scoped keys get 401. |
Response 200
{
"collections": [
{
"id": "uuid",
"slug": "pubpub-archive",
"name": "PubPub Archive",
"public": true,
"ownerSlug": "knowledge-futures",
"ownerName": "Knowledge Futures",
"createdAt": "2026-01-15T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z",
"description": "Full archive of PubPub publications",
"tags": ["publishing"],
"latestVersion": "v3.2.0",
"recordCount": 4521,
"fileCount": 892,
"totalBytes": 1073741824,
"lastPushAt": "2026-04-01T00:00:00.000Z"
}
],
"facets": {
"owners": [{ "slug": "knowledge-futures", "name": "Knowledge Futures", "count": 12 }],
"tags": [{ "name": "publishing", "count": 4 }]
},
"featuredTags": ["publishing"],
"featuredCollections": []
}Counts are for the caller: members of the owning organization see totals that include private records and files; everyone else sees the public ones.
POST /api/accounts/:owner/collections
Auth: member of the account, by session or a write or admin key not scoped to specific collections
Create a new collection under an account. Only slug is required. name defaults to the slug. public defaults to false. description is optional; it shows until a version’s metadata gives one.
Request
{
"slug": "my-dataset",
"name": "My Dataset",
"description": "What the collection holds",
"public": true
}Response 201
{
"id": "uuid",
"owner": "yourname",
"slug": "my-dataset",
"name": "My Dataset"
}Errors
401 | Not authenticated. |
403 | Not a member of the account, or a read key or a key scoped to specific collections. |
404 | No account with that slug. |
409 | The account already has a collection with this slug. |
422 | The slug is missing or not a valid slug. |
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",
"public": true,
"ownerSlug": "knowledge-futures",
"ownerName": "Knowledge Futures",
"createdAt": "2026-01-15T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z",
"description": "Full archive of PubPub publications",
"ark": "https://www.underlay.org/ark:12345/ulb9bq4n5gmv3k0",
"versionCount": 14,
"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"
}
}Counts are for the caller: recordCount, fileCount and totalBytes include private records and files for the collection’s members, and leave them out for everyone else. ark is null when the collection’s ARK is off. latestVersion is null before the first push.
PATCH /api/collections/:owner/:slug
Auth: write access (a member, by session or a write or admin key); changing public also needs the owner or admin role, by session or an admin key not scoped to specific collections
Update a collection’s name, slug or public. Pass only the fields to change. The response gives the collection’s slug after the change.
Request
{
"name": "New Name",
"public": false
}Response 200
{ "ok": true, "slug": "pubpub-archive" }Errors
403 | No write access, or a change to public without the owner or admin role (a key scoped to specific collections never has it). |
409 | The account already has a collection with the new slug. |
422 | The new slug is not a valid slug. |
DELETE /api/collections/:owner/:slug
Auth: owner or admin of the owning organization, by session or an admin key (a write key, or any key scoped to specific collections, acts as a member and gets 403)
Delete a collection with its versions, push sessions and webhooks. Stored records and files are not deleted with it (they may be shared with other collections).
Response 200
{ "ok": true }GET /api/accounts/:owner/collections
No auth required
List an account’s collections, most recently updated first. Members of the account see all of them; everyone else sees only public ones. An unknown account returns 200 with an empty list.
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"
}
]POST /api/collections/:owner/:slug/metadata
Auth: write access
Update version metadata by creating a new patch version. The request body is a JSON object (at most 8 MiB) 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; null removes a field. |
Response 201
{
"semver": "v3.2.1",
"hash": "ulv2:e5f6a7b8...",
"status": "completed"
}When nothing changes, the response is 200 { "semver", "unchanged": true } and no version is made.
Errors
400 | The body is not a JSON object, or breaks the input rules. |
409 | Version conflict: another version was published while this one was made. Retry. |
413 | The body is over 8 MiB. |
422 | No versions exist yet. Push a version first before updating metadata. |
POST /api/collections/:owner/:slug/transfer
Auth: owner or admin of both the current and the target organization, by session or an admin key not scoped to specific collections
Move a collection to another organization. Its slug stays the same, so the target must not already have a collection with that slug.
Request
{ "targetOrgSlug": "my-org" }Response 200
{ "ok": true, "newOwner": "my-org" }Errors
400 | targetOrgSlug is missing. |
403 | Not an owner or admin of the collection’s organization or of the target. |
404 | Collection not found, or no organization with the target slug. |
409 | The target organization already has a collection with this slug. |
POST /api/collections/:owner/:slug/fork
Auth: member of the target organization, by session or a write or admin key not scoped to specific collections
Fork any collection you can read into a target organization. The fork is a new, private collection whose first version, v1.0.0 with the message Forked from <slug> <semver>, reuses the source’s latest version. Records, schemas, and files are referenced, not copied, so a fork takes no additional storage. Both collections must share a storage location.
A member of the source’s organization forks both its public and its private records. Anyone else forks the public records only.
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
400 | targetOrg is missing. |
401 | Not authenticated. |
403 | Not a member of the target org, or a read key or a key scoped to specific collections. |
404 | Source collection not found or not readable by you, or target org not found. |
409 | A collection with the same slug already exists in the target org. |
422 | The slug is not a valid slug, or the source collection has no versions to fork. |