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

qCollections whose name contains this text
ownerOnly collections of this account (its slug)
tagOnly collections with this tag
sortname or records; by default, most recently updated first
limitMax results (default 50, max 100)
offsetPagination offset
minetrue 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

401Not authenticated.
403Not a member of the account, or a read key or a key scoped to specific collections.
404No account with that slug.
409The account already has a collection with this slug.
422The 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

403No write access, or a change to public without the owner or admin role (a key scoped to specific collections never has it).
409The account already has a collection with the new slug.
422The 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

descriptionShort description of the collection.
readmeMarkdown readme content.
licenseLicense 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

400The body is not a JSON object, or breaks the input rules.
409Version conflict: another version was published while this one was made. Retry.
413The body is over 8 MiB.
422No 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

400targetOrgSlug is missing.
403Not an owner or admin of the collection’s organization or of the target.
404Collection not found, or no organization with the target slug.
409The 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

targetOrgRequired. Slug of the organization to fork into. You must be a member of this org.
slugOptional 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

400targetOrg is missing.
401Not authenticated.
403Not a member of the target org, or a read key or a key scoped to specific collections.
404Source collection not found or not readable by you, or target org not found.
409A collection with the same slug already exists in the target org.
422The slug is not a valid slug, or the source collection has no versions to fork.