Organize documents in folders

Folders keep documents where people expect to find them in the Circularo web app. This guide creates a folder, files a document into it, publishes the same document to the organization's tree, reads back where it ended up, and lists the folders it can see. It takes five calls.

Before you begin

  • A document to file. Any document works; the examples use an employment agreement.

Two folder trees

Every folder belongs to one of two trees, and the scope of a folder says which:

Scope

What it is

personal

your own tree

shared

your organization's tree

A folder belongs to its tree for its whole life; there is no way to hand one tree's folder to the other. A document is different: it holds one placement per tree, so the same document can sit in a folder of your personal tree and, at the same time, somewhere in the shared tree. A placement is a pair — the scope and the folderId inside it, where null means the root of that tree.

Step 1: Create a folder

A folder needs a name and the tree it lives in:

POST /folders
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
    "name": "Employment agreements",
    "scope": "personal"
}
HTTP/2 201

{
    "createdAt": "2026-05-11T09:30:00.000Z",
    "createdBy": "john.smith@example.com",
    "id": "Th5nQd8XcW2bRv6yLpKm",
    "name": "Employment agreements",
    "parentFolderId": null,
    "scope": "personal",
    "trashed": false,
    "webUrl": "https://sandbox.circularo.com/home/my_files?folderId=Th5nQd8XcW2bRv6yLpKm"
}

parentFolderId is null, so this folder sits at the root of the personal tree. Pass one to create a folder inside another. Keep the id — filing a document means naming that folder.

Step 2: File the document into it

A document is placed one tree at a time, and the tree is part of the path:

PUT /documents/pXq4NcYBhK9tWdA2mFzR/folders/personal
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
    "folderId": "Th5nQd8XcW2bRv6yLpKm"
}
HTTP/2 204

The same call moves a document that is already in that tree, so there is no separate move operation. Sending folderId: null places the document at the root of the tree.

Step 3: Put the document in the shared tree as well

Placing it in the other tree leaves the first placement alone:

PUT /documents/pXq4NcYBhK9tWdA2mFzR/folders/shared
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
    "folderId": null
}
HTTP/2 204
Important

In the shared tree a document also takes on the access rights of the folder it lands in, so filing it there can widen who is able to see it. Moving it away afterwards does not narrow those rights again.

Step 4: Read where the document sits

The document itself reports its placements:

GET /documents/pXq4NcYBhK9tWdA2mFzR
Authorization: Bearer YOUR_API_KEY
HTTP/2 200

{
    "id": "pXq4NcYBhK9tWdA2mFzR",
    "title": "Employment Agreement (Jane Doe)",
    "folders": [
        {
            "folderId": "Th5nQd8XcW2bRv6yLpKm",
            "scope": "personal"
        },
        {
            "folderId": null,
            "scope": "shared"
        }
    ],
    ...
}

One entry per tree the document belongs to — here a folder of the personal tree, and the root of the shared one.

To take a document out of one tree, delete that placement: DELETE /documents/{id}/folders/shared removes the shared entry and leaves the personal one untouched. Removing the last one leaves folders empty; the document is then unfiled, which is a perfectly normal state — it is still readable, searchable and signable, it just sits in no folder.

Step 5: Find your folders again

Folders are their own collection, with their own search:

GET /folders?scope=personal
Authorization: Bearer YOUR_API_KEY
HTTP/2 200

{
    "total": 1,
    "pagination": {
        "limit": 20,
        "nextCursor": null,
        "offset": 0
    },
    "results": [
        {
            "id": "Th5nQd8XcW2bRv6yLpKm",
            "name": "Employment agreements",
            "parentFolderId": null,
            "scope": "personal",
            ...
        }
    ]
}

The two collections never mix: this search returns folders and never documents, while GET /documents?folderId=… returns the documents placed directly in a folder — and with recursive=true everything nested deeper. Walking a tree therefore means asking twice per level, once for each collection.

Narrow the folder search the same way: scope for a whole tree, parentFolderId for the folders directly inside one, recursive to reach any depth, and name to look one up by what it is called. A folder itself is moved with PUT /folders/{id}/parent, naming the new parent, or null for the root of its tree.

Caution

DELETE /folders/{id} takes the folder's whole subtree with it — the folders inside it and their documents all go to the trash together, or are removed for good when you pass permanent.

A trashed folder comes back the way a trashed document does: POST /folders/{id}/restore takes it out of the trash together with everything that went in with it. When you already hold a folder's identifier, GET /folders/{id} reads that one folder without a search.

Complete example

Filing a document into a new folder, as a Node.js script. Error handling is minimal for brevity — in production, inspect the error envelope of non-2xx responses.

JavaScript
const BASE_URL = "https://sandbox.circularo.com/api/v1/public"; // your instance's base URL
const API_KEY = "YOUR_API_KEY";
const HEADERS = { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" };
const documentId = "pXq4NcYBhK9tWdA2mFzR"; // the document to file

const folderRes = await fetch(`${BASE_URL}/folders`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
        name: "Employment agreements",
        scope: "personal"
    })
});
if (!folderRes.ok) throw new Error(`Folder failed: ${folderRes.status}`); // error handling kept minimal for brevity

const folder = await folderRes.json();
await fetch(`${BASE_URL}/documents/${documentId}/folders/${folder.scope}`, {
    method: "PUT",
    headers: HEADERS,
    body: JSON.stringify({ folderId: folder.id })
});

console.log(`Document filed into ${folder.name}`);

Next steps