Folders API Overview
Endpoints for organizing content into hierarchical Folder structures. A Folder
can hold Reports, Datasets, Datasources, Jobs, Queries, Templates, Assistants,
Libraries, Integrations, and Toolkits, and can nest under a parent Folder to any
depth. All routes are prefixed with /api.
A Folder carries no owner column, so any Folder in the Tenant is readable by an
authenticated caller (read access is enforced by the model's read_access
scope). Creating Folders requires permission.folders.create, whose driver is
isDesigner(): Designers and superusers pass, ordinary users do not.
GET /api/folders
List the Tenant's Folders, with optional name search.
Authentication: Required (session or API token)
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
q | string | Partial, case-insensitive match against the Folder name |
Response:
A HAL collection under _embedded["inf:folder"]. There is no server-side sort,
so rows come back in database order. Each Folder carries a permissions map
(edit, delete, write, rename) for the caller, and a Folder that has a
parent also exposes an inf:parent-folder link.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders"
}
},
"start": 0,
"count": 5,
"total": 5,
"_embedded": {
"inf:folder": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000001"
}
},
"permissions": {
"edit": true,
"delete": true,
"write": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "Engineering",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"parentId": null
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000002"
}
},
"permissions": {
"edit": true,
"delete": true,
"write": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000002",
"name": "Marketing Reports",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"parentId": null
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000003"
}
},
"permissions": {
"edit": true,
"delete": true,
"write": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000003",
"name": "Q1 2024",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"parentId": "00000000-0000-4000-8000-000000000004"
},
"… 2 more items (5 total) — omitted from docs"
]
}
}
Pass q to filter by name. The shape is identical to the unfiltered list.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders"
}
},
"start": 0,
"count": 0,
"total": 0,
"_embedded": {
"inf:folder": []
}
}
POST /api/folders
Create a Folder.
Authentication: Required (session or API token)
Pre-blocks: permission.folders.create
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Folder name |
parentId | string (UUID) | null | No | Parent Folder id. Pass null (or omit it) for a root Folder |
Example Request:
{
"name": "Marketing",
"parentId": null
}
Response:
Responds 201 Created with the Folder and a Location header pointing at the
new resource.
{
"name": "Marketing",
"parentId": null
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000001"
}
},
"permissions": {
"edit": true,
"delete": true,
"write": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000001",
"tenant": "acme",
"name": "Marketing",
"parentId": null,
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z",
"_altid": null
}
GET /api/folders/{id}
Retrieve a single Folder.
Authentication: Required (session or API token)
Pre-blocks: folder.lookup (resolves through the read_access scope)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Folder id |
Response:
The Folder links to inf:permissions; a Folder that has a parent also links
inf:parent-folder. The example below is the child "Q1 2024" Folder, so its
parent link is shown.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000001"
},
"inf:permissions": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000001/permissions"
},
"inf:parent-folder": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000002"
}
},
"permissions": {
"edit": true,
"delete": true,
"write": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "Q1 2024",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"parentId": "00000000-0000-4000-8000-000000000002"
}
PUT /api/folders/{id}
Update a Folder's name or parent.
Authentication: Required (session or API token)
Pre-blocks: folder.lookup
Request Body (partial):
| Field | Type | Description |
|---|---|---|
name | string | New Folder name |
parentId | string (UUID) | null | New parent Folder id. Pass null to move the Folder to the root |
Example Request:
{
"name": "Engineering & Platform",
"parentId": null
}
Response:
Responds 200 with the re-fetched Folder.
{
"name": "Engineering & Platform",
"parentId": null
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000001"
}
},
"permissions": {
"edit": true,
"delete": true,
"write": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "Engineering & Platform",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"parentId": null
}
DELETE /api/folders/{id}
Delete a Folder.
Authentication: Required (session or API token)
Pre-blocks: folder.lookup
Response:
Responds 200 with an empty body (Informer's db.remove convention), not 204.
Deleting a Folder cascades to its child Folders. Entities contained in the
deleted Folder are not removed: their folderId is set to null, leaving them
in the system but un-foldered.
GET /api/folders/{id}/folders
List the direct subfolders of a Folder.
Authentication: Required (session or API token)
Pre-blocks: folder.lookup
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Parent Folder id |
Response:
A HAL collection under _embedded["inf:folder"], filtered to the Folders whose
parentId equals {id}.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000001/folders"
}
},
"start": 0,
"count": 1,
"total": 1,
"_embedded": {
"inf:folder": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000002"
}
},
"permissions": {
"edit": true,
"delete": true,
"write": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000002",
"name": "Q1 2024",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"parentId": "00000000-0000-4000-8000-000000000001"
}
]
}
}
The subfolders route is /api/folders/\{id\}/folders, not /api/folders/\{id\}/subfolders.
POST /api/folders/{id}/folders
Create a child Folder under {id}.
Authentication: Required (session or API token)
Pre-blocks: permission.folders.create
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Folder name |
The route forces parentId = {id} from the URL regardless of the body, so the
new Folder is always a child of {id}.
Example Request:
{
"name": "Q2 2024"
}
Response:
Responds 201 Created with the Folder and a Location header.
{
"name": "Q2 2024"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000002"
}
},
"permissions": {
"edit": true,
"delete": true,
"write": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000002",
"tenant": "acme",
"name": "Q2 2024",
"parentId": "00000000-0000-4000-8000-000000000001",
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z",
"_altid": null
}
GET /api/folders/{id}/permissions
Get the caller's permission map for a Folder.
Authentication: Required (session or API token)
Pre-blocks: folder.lookup
Response:
A flat object of the caller's booleans for the folders permission group
(edit, delete, write, rename, create), alongside the HAL self link.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/folders/00000000-0000-4000-8000-000000000001/permissions"
}
},
"edit": true,
"delete": true,
"write": true,
"rename": true
}
POST /api/folders-bulk
Add, remove, and update Folders in a single transaction.
Authentication: Required (session or API token)
Pre-blocks: permission.folders.create
Request Body:
| Field | Type | Description |
|---|---|---|
add | array | Folders to create. Defaults to [] |
remove | array | Folder ids (UUIDs) to delete. Defaults to [] |
update | array | Folders to update. Defaults to [] |
add and update array items:
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Folder id. For add, this is a client-generated UUID |
name | string | Folder name |
parentId | string (UUID) | null | Parent Folder id |
Example Request:
{
"add": [
{ "id": "7a2c5f3e-1b4d-4c6a-8e9f-0a1b2c3d4e5f", "name": "2024 Reports", "parentId": null }
],
"remove": [
"old-folder-uuid"
],
"update": [
{ "id": "sales-folder-uuid", "name": "Sales & Revenue Reports", "parentId": null }
]
}
Processing order: remove (which also cleans up child Folders), then add, then update.
Response:
Responds 200 with an empty body, not 204.
{
"add": [
{
"id": "00000000-0000-4000-8000-000000000001",
"name": "2024 Reports",
"parentId": null
}
],
"remove": [
"00000000-0000-4000-8000-000000000002"
],
"update": [
{
"id": "00000000-0000-4000-8000-000000000003",
"name": "Sales & Revenue Reports",
"parentId": null
}
]
}
The bulk route is /api/folders-bulk, not /api/folders/_bulk-update.
POST /api/folders-bulk-assign
Assign a list of entities to a Folder (or remove them from any Folder), in a single transaction.
Authentication: Required (session or API token)
Request Body:
| Field | Type | Description |
|---|---|---|
entities | array | Entity ids (UUIDs) to assign. Defaults to [] |
folderId | string (UUID) | null | Target Folder id. Pass null to remove the entities from any Folder |
Example Request:
{
"entities": [
"report-uuid",
"dataset-uuid",
"job-uuid"
],
"folderId": "sales-folder-uuid"
}
Each entity is looked up under its own read_access scope and pruned unless the
caller has <type>:write on it. Supported types are Reports, Datasets,
Datasources, Jobs, Queries, Templates, Assistants, Libraries, Integrations, and
Toolkits.
Response:
Responds 200 with an empty body. The captured example uses an empty
entities list to show the contract without seeding cross-module content.
{
"entities": [],
"folderId": "00000000-0000-4000-8000-000000000001"
}
The bulk-assign route is /api/folders-bulk-assign, not /api/folders/_bulk-assign.
Folder Hierarchy
Folders support nesting to any depth. A child Folder references its parent via
parentId, and the serialized Folder exposes an inf:parent-folder link when a
parent is set:
Root
├── Sales Reports
│ ├── Q1 2024
│ │ ├── January
│ │ └── February
│ └── Q2 2024
└── Marketing Reports
Supported Entity Types
The bulk-assign route can place these entity types into a Folder, subject to a
per-entity <type>:write check:
- Reports
- Datasets
- Datasources
- Jobs
- Queries
- Templates
- Assistants
- Libraries
- Integrations
- Toolkits