Skip to main content

Favorites

Endpoints for the aggregate Favorites view and the bulk favorite toggle. Both live at /api/favorites.

Despite the name, GET /api/favorites is not a list of Favorite rows. It is the home/landing aggregate: every entity the caller can read across all types (Datasets, Datasources, Reports, adhoc Queries, Jobs, Assistants, Libraries, Apps, Integrations, and Toolkits), each decorated with a favorite boolean. The client filters to favorite: true to render the Favorites view, but the route returns the full readable set. POST /api/favorites is the bulk toggle that marks or unmarks entities as favorites for the current User.

A favorite is always scoped to the caller. The owning principal is taken from the authenticated session (req.auth.credentials.username), never from the payload, so one User favoriting an entity never affects another User's view.

GET /api/favorites

Read the home aggregate: every entity the caller can read across all types, each decorated with its favorite status.

Authentication: Authenticated session only. There is no permission pre-block and no license gate. Rows are filtered by each entity type's own read_access, so a caller never sees an entity they cannot read, even if another User favorited it.

Query Parameters:

ParameterTypeDefaultDescription
teamIdstring-Narrow the aggregate to rows owned by this team (item.ownerId === teamId). The only accepted query param.

Response:

A HAL collection under _embedded["inf:favorites"]. Each row carries objectType, typeName (an i18n display label), image (the type's icon URL), tags (TagEntity ids), permissions, and a favorite boolean. System Jobs are excluded, and Apps are de-duplicated so each App appears once.

List the home aggregate (readable entities with favorite status)GET /api/favorites
GET /api/favorites is a HAL collection (rel inf:favorites) of EVERY entity the caller can read across all types (datasets, datasources, reports, adhoc queries, jobs, assistants, libraries, apps, integrations, toolkits). It is not a list of favorite rows: each row carries objectType, typeName, image, tags, permissions, and a `favorite` boolean, and the client filters to favorite:true to render the Favorites view. No permission check beyond an authenticated session; rows are scoped by each type’s read_access, and system jobs are excluded.
Response · 200
[
{
"id": "00000000-0000-4000-8000-000000000001",
"slug": "reorder-points",
"name": "Reorder Points",
"description": "Stock thresholds by product",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"dataUpdatedAt": "2026-01-15T16:20:00.000Z",
"shared": false,
"esType": "data",
"esIndex": "docsex.acme.reorder-points.1700000000000",
"ownerId": "admin",
"datasetFieldReportAccess": false,
"username": "admin",
"displayName": "acme Administrator",
"ownerName": "acme Administrator",
"favorite": true,
"permissions": {
"edit": true,
"share": true,
"delete": true,
"write": true,
"addVisual": true,
"deleteVisual": true,
"changeOwner": true,
"copy": true,
"rename": true,
"refresh": true,
"modifyFlows": true,
"replaceFile": true,
"modifySettings": true,
"createDataView": true,
"assignTags": true,
"createFilter": true,
"scriptFields": true,
"setIndex": false
},
"naturalId": "admin:reorder-points",
"objectType": "dataset",
"typeName": "Dataset",
"image": "/images/icons/dataset.svg",
"tags": []
}
]
Captured from the API examples test suite; ids and timestamps are normalized.
Not a list of Favorite rows

The rows render under _embedded["inf:favorites"], not a top-level array, and the set spans every readable type rather than just the caller's favorites. Filter client-side on favorite: true to build the Favorites view.

Order is not deterministic

The handler returns rows in database order with no cross-type ORDER BY, so multi-row results are not stably ordered run to run. Sort client-side if you need a deterministic order.


GET /api/favorites?teamId={team}

Filtering by teamId narrows the aggregate to rows whose ownerId is that team. In the captured example the seeded Dataset is owned by a User rather than a team, so the filtered collection is empty, which demonstrates the filter shape.

Filter the aggregate by owning teamGET /api/favorites?teamId=marketing
GET /api/favorites?teamId={team} narrows the aggregate to rows whose ownerId is that team. `teamId` is the only accepted query param. Here the seeded dataset is owned by the `admin` user, not a team, so the filtered collection is empty — demonstrating the filter shape.
Response · 200 · empty list (no items in this example)
Captured from the API examples test suite; ids and timestamps are normalized.

POST /api/favorites

Bulk favorite toggle for the current User. With favorite: true it upserts a Favorite row for each supplied id; with favorite: false it destroys the caller's matching Favorite rows.

Authentication: Authenticated session only. The favorite is always scoped to the caller, whose principal comes from the session and never from the payload.

Request Body:

FieldTypeRequiredDescription
favoritebooleanYestrue to mark as favorites, false to remove
reportsstring[]NoReport ids
queriesstring[]NoAdhoc Query ids
datasetsstring[]NoDataset ids
datasourcesstring[]NoDatasource ids
jobsstring[]NoJob ids
assistantsstring[]NoAssistant ids
templatesstring[]NoTemplate ids
librariesstring[]NoLibrary ids
integrationsstring[]NoIntegration ids
toolkitsstring[]NoToolkit ids
appsstring[]NoApp ids

The payload is stripUnknown, so any non-whitelisted key is dropped. The route runs in a transaction.

Example Request:

{ "favorite": true, "datasets": ["00000000-0000-4000-8000-000000000001"] }

Response:

Responds 200 with an empty body. This is the Informer convention for a handler that returns nothing: it is not 201 (there is no Location) and not 204. Both the mark and the unmark paths answer 200 with an empty body. Marking with favorite: true is idempotent (the upsert keys are [tenant, <type>Id, principalId]), and removing an id that was not favorited is a no-op 200.

Mark entities as favoritesPOST /api/favorites
POST /api/favorites is the bulk favorite toggle for the current user. With favorite:true it upserts a Favorite row for each id in the per-type arrays (reports, queries, datasets, datasources, jobs, templates, libraries, integrations, toolkits, apps), scoped to the caller (principalId is taken from the session, never the payload). It is idempotent and runs in a transaction. Responds 200 with an EMPTY body (not 201, not 204). Unknown payload keys are stripped.
Request body
{
"favorite": true,
"datasets": [
"00000000-0000-4000-8000-000000000001"
]
}
Response · 200 · no body
Captured from the API examples test suite; ids and timestamps are normalized.

POST /api/favorites (remove)

The unset half of the toggle. Sending favorite: false with the same per-type id arrays destroys the caller's Favorite rows for those ids.

Example Request:

{ "favorite": false, "datasets": ["00000000-0000-4000-8000-000000000001"] }
Remove entities from favoritesPOST /api/favorites
POST /api/favorites with favorite:false destroys the caller’s Favorite rows for the supplied ids (same payload shape as the assign call). It is the unset half of the toggle. Responds 200 with an EMPTY body. Removing an id that was not favorited is a no-op 200.
Request body
{
"favorite": false,
"datasets": [
"00000000-0000-4000-8000-000000000001"
]
}
Response · 200 · no body
Captured from the API examples test suite; ids and timestamps are normalized.

POST /api/favorites (Assistant)

Assistants are favorited through the assistants array, exactly like the other per-type id arrays.

Example Request:

{ "favorite": true, "assistants": ["00000000-0000-4000-8000-000000000001"] }
Favorite an assistantPOST /api/favorites
Assistants are favorited through the `assistants` array (the field name matches the joi payload and every sibling type). Responds 200 with an EMPTY body, like any other favorite assign. NOTE: the favorite is silent — a 200 does not echo the row — so confirm via GET /api/favorites (the assistant row’s `favorite` flips to true).
Request body
{
"favorite": true,
"assistants": [
"00000000-0000-4000-8000-000000000001"
]
}
Response · 200 · no body
Captured from the API examples test suite; ids and timestamps are normalized.
Favorites are silent

The 200 response has an empty body and does not echo the favorited row, so confirm the change took by re-reading GET /api/favorites and checking the Assistant row's favorite flag.


Per-entity favorite routes

The toggle above is the bulk surface. Each entity type also exposes its own favorite route (for example PUT and DELETE /api/assistants/\{id\}/favorite), documented on that entity's page rather than here. Collection-level routes such as Comments are likewise documented on the owning entity's page.