Collections API

Collections group documents in a team’s library. Use them to gather a series, a reading list, or a curated set that readers can filter the library by.

Authentication

All endpoints require a Bearer token. See Getting Started.

Authorization: Bearer <your-api-key>

A key acts as the team member who created it, so it sees and manages exactly what that person does in the app.

Scopes and permissions

A collection has one of three scopes, and the scope decides who can see it and who may change it:

ScopeVisible toManaged by
privateIts creator only.Its creator.
sharedIts creator and the team members invited to it.Its creator.
organizationEvery member of the team.Owners, super admins, developers, and content admins.

Creating or changing an organization collection needs a key belonging to an owner, super admin, developer, or content admin. A member’s key can still create private collections.

A document may only join a collection at or below its own scope: an organization document fits any collection, a shared document fits private and shared collections, and a private document only fits its own uploader’s private collections.

List collections

GET /api/document-collections

Returns the collections you can see in a team, ordered by scope (private, then shared, then organization) and alphabetically by name within each scope. Use it to find a collection’s id before applying it to documents.

Query parameters

ParameterTypeDefaultDescription
teamstringTeam slug. Required unless the API key is already scoped to one team.
scopestringallRestrict to private, shared, or organization. Repeatable.
idstringA collection UUID, to fetch one collection.
filterstringCase-insensitive substring match on the name.
pagenumber1Page to return.
per_pagenumber100Results per page.
viewable_countsbooleanfalseReport document_count as the number of published documents you can actually read, rather than every linked document.
has_viewable_documentsbooleanfalseDrop collections that contain no published document you can read.

Response

{
  "data": [
    {
      "id": "col-abc123",
      "name": "Encyclicals",
      "scope": "organization",
      "user_id": null,
      "team_id": "team-xyz789",
      "created_at": "2026-01-14T09:31:02.184Z",
      "team": {
        "id": "team-xyz789",
        "name": "Magisterium",
        "slug": "magisterium"
      },
      "document_count": [{ "count": 42 }],
      "userCanDelete": true,
      "canLeaveSharedCollection": false
    }
  ],
  "error": null,
  "pagination": { "pageIndex": 0, "pageSize": 100, "pageCount": 1 }
}

user_id is null on an organization collection, which the team owns rather than any one person. userCanDelete reports whether your key may delete this collection, and canLeaveSharedCollection whether you are an invitee who could leave it.

Create a collection

POST /api/document-collections

Request body

FieldTypeDescription
namestringCollection name. Required.
scopestringprivate, shared, or organization. Defaults to private.
teamstringTeam slug. Required unless the API key is already scoped to one team.
curl -X POST https://vulgate.ai/api/document-collections \
  -H "Authorization: Bearer $VULGATE_TEAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Encyclicals", "scope": "organization", "team": "magisterium" }'

Response

{
  "data": {
    "id": "col-abc123",
    "name": "Encyclicals",
    "scope": "organization",
    "user_id": null,
    "team_id": "team-xyz789",
    "created_at": "2026-01-14T09:31:02.184Z"
  }
}
StatusMeaning
200Collection created.
400Missing or invalid name, scope, or team.
403Your key may not create a collection at this scope, or the organization is read-only.
404Team not found.
429Your own private and shared collections have reached the limit of 100.

The limit of 100 is per user per team and covers only the collections you own. organization collections are team-owned and are not capped.

List a collection’s documents

Filtering the library by collection lives on the Documents API, in library mode:

GET /api/documents?library=true&collection={collection_id}

collection takes a collection UUID and is repeatable, returning documents in any of the collections you name. It is only recognized with library=true.

Apply a collection to documents

Two endpoints write the link between documents and collections. Pick by which side you are working from.

Add documents to one collection

POST /api/collections/{collection_id}/add-documents

Adds to whatever is already there. Documents already in the collection are skipped rather than rejected, so the call is safe to repeat.

curl -X POST https://vulgate.ai/api/collections/col-abc123/add-documents \
  -H "Authorization: Bearer $VULGATE_TEAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "document_ids": ["doc-xyz789", "doc-xyz790"] }'
{ "data": null, "error": null }

When every id was already linked, the response reports what it skipped instead:

{ "data": { "success": true, "added": 0, "skipped": 2 }, "error": null }
StatusMeaning
200Links written (or already present).
400Empty document_ids, an unknown or unpublished document, a document outside the collection’s organization, or a document whose scope does not fit the collection.
403Your key may not manage this collection.
404Collection not found.

Only published documents can be added. Publish the document first, then add it.

Set one document’s collections

PATCH /api/documents/{document_id}

Send document_collections to replace a document’s collection membership wholesale — ids you omit are unlinked:

curl -X PATCH https://vulgate.ai/api/documents/doc-xyz789 \
  -H "Authorization: Bearer $VULGATE_TEAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "document_collections": ["col-abc123"] }'

Pass [] to remove the document from every collection. See the Documents API for the rest of the request body.

Do not confuse the two similarly named fields: document_collections is the list of collection ids a document belongs to, while collections is free-text bibliographic metadata — the series or set a work was published in — and has no effect on collection membership.