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:
| Scope | Visible to | Managed by |
|---|---|---|
private | Its creator only. | Its creator. |
shared | Its creator and the team members invited to it. | Its creator. |
organization | Every 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
| Parameter | Type | Default | Description |
|---|---|---|---|
team | string | — | Team slug. Required unless the API key is already scoped to one team. |
scope | string | all | Restrict to private, shared, or organization. Repeatable. |
id | string | — | A collection UUID, to fetch one collection. |
filter | string | — | Case-insensitive substring match on the name. |
page | number | 1 | Page to return. |
per_page | number | 100 | Results per page. |
viewable_counts | boolean | false | Report document_count as the number of published documents you can actually read, rather than every linked document. |
has_viewable_documents | boolean | false | Drop 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
| Field | Type | Description |
|---|---|---|
name | string | Collection name. Required. |
scope | string | private, shared, or organization. Defaults to private. |
team | string | Team 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"
}
}
| Status | Meaning |
|---|---|
200 | Collection created. |
400 | Missing or invalid name, scope, or team. |
403 | Your key may not create a collection at this scope, or the organization is read-only. |
404 | Team not found. |
429 | Your 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 }
| Status | Meaning |
|---|---|
200 | Links written (or already present). |
400 | Empty document_ids, an unknown or unpublished document, a document outside the collection’s organization, or a document whose scope does not fit the collection. |
403 | Your key may not manage this collection. |
404 | Collection 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.