Tags & Favorites
Endpoints for tagging a Query (a shared, organization-wide label) and for bookmarking a Query as a personal favorite (scoped to the current user).
Every route on this page resolves the Query through query.lookup (the
read_access model scope), so an id the caller may not see answers 404. The
tag routes reference a Tag by its UUID, not by a slug or name. Assigning or
removing a tag requires the query:assignTags permission; the favorite routes
require nothing beyond read access to the Query, and always act on the calling
user's own favorite.
{tagId} is the Tag's UUID, the foreign key to tag.id. There is no slug-based
addressing such as finance or quarterly. Create or look up the Tag first to
obtain its UUID, then use that value in the path.
Tags
Tags categorize Queries across the organization. A tag assignment is a join row
(a Tag Entity) binding a tagId to a queryId.
GET /api/queries/{id}/tags
List the tags assigned to a Query.
Authentication: Required (read access to the Query)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query UUID or natural id (ownerId:slug) |
Response:
A HAL collection (rel inf:query-tags) embedding inf:tag-entity join rows.
Each join row carries queryId and tagId and embeds its inf:tag under
_embedded. The start/count/total fields are the standard HAL collection
envelope.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/queries/admin%3Aregional-sales/tags"
}
},
"start": 0,
"count": 1,
"total": 1,
"_embedded": {
"inf:tag-entity": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/queries/00000000-0000-4000-8000-000000000001/tags/00000000-0000-4000-8000-000000000002"
}
},
"id": "00000000-0000-4000-8000-000000000003",
"tagId": "00000000-0000-4000-8000-000000000002",
"datasetId": null,
"queryId": "00000000-0000-4000-8000-000000000001",
"reportId": null,
"templateId": null,
"datasourceId": null,
"jobId": null,
"assistantId": null,
"libraryId": null,
"appId": null,
"toolkitId": null,
"integrationId": null,
"source": null,
"sourceId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"_embedded": {
"inf:tag": {
"_links": {
"self": {
"href": "https://informer.example.com/api/tags/00000000-0000-4000-8000-000000000002"
}
},
"permissions": {
"edit": true,
"delete": true,
"write": true,
"rename": true
},
"id": "00000000-0000-4000-8000-000000000002",
"tenant": "acme",
"name": "Finance",
"color": "blue",
"source": null,
"sourceId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
}
}
]
}
}
GET /api/queries/{id}/tags/{tagId}
Get a single tag assignment for a Query.
Authentication: Required (read access to the Query)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query UUID or natural id (ownerId:slug) |
tagId | string | Tag UUID |
Response:
The single Tag Entity join row for that Query and Tag, carrying inf:query and
inf:tag links. Returns 404 if the tag is not assigned to the Query.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/queries/00000000-0000-4000-8000-000000000001/tags/00000000-0000-4000-8000-000000000002"
},
"inf:query": {
"href": "https://informer.example.com/api/queries/00000000-0000-4000-8000-000000000001"
},
"inf:tag": {
"href": "https://informer.example.com/api/tags/00000000-0000-4000-8000-000000000002"
}
},
"id": "00000000-0000-4000-8000-000000000003",
"tagId": "00000000-0000-4000-8000-000000000002",
"datasetId": null,
"queryId": "00000000-0000-4000-8000-000000000001",
"reportId": null,
"templateId": null,
"datasourceId": null,
"jobId": null,
"assistantId": null,
"libraryId": null,
"appId": null,
"toolkitId": null,
"integrationId": null,
"source": null,
"sourceId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
}
PUT /api/queries/{id}/tags/{tagId}
Assign a tag to a Query.
Authentication: Required
Permission: query:assignTags
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query UUID or natural id (ownerId:slug) |
tagId | string | Tag UUID |
Response:
Upserts the Tag Entity join row and responds 200 with the row (not 201). The
operation is idempotent; re-assigning an already-assigned tag returns the
existing row.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/queries/00000000-0000-4000-8000-000000000001/tags/00000000-0000-4000-8000-000000000002"
}
},
"id": "00000000-0000-4000-8000-000000000003",
"tenant": "acme",
"tagId": "00000000-0000-4000-8000-000000000002",
"queryId": "00000000-0000-4000-8000-000000000001",
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z",
"_altid": null,
"datasetId": null,
"reportId": null,
"templateId": null,
"source": null,
"sourceId": null,
"datasourceId": null,
"jobId": null,
"assistantId": null,
"libraryId": null,
"toolkitId": null,
"integrationId": null,
"appId": null
}
There is no application-level check that the Tag exists, but tagId is a foreign
key to tag.id. Assigning an unknown UUID fails at the database, so create the
Tag (POST /api/tags) before assigning it.
DELETE /api/queries/{id}/tags/{tagId}
Remove a tag from a Query.
Authentication: Required
Permission: query:assignTags
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query UUID or natural id (ownerId:slug) |
tagId | string | Tag UUID |
Response:
Removes the Tag Entity join row and responds 200 with an empty body (Informer
convention), not 204. Removing a tag that is not assigned is a no-op 200.
Favorites
Bookmark a Query for quick access. A favorite is always scoped to the current
user: the principalId is derived from the authenticated identity
(req.auth.credentials.username) and is never client-supplied.
GET /api/queries/{id}/favorite
Check whether the current user has favorited a Query.
Authentication: Required (read access to the Query)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query UUID or natural id (ownerId:slug) |
Response:
The current user's Favorite row for this Query: id, principalId, queryId,
the other nullable entity foreign keys, and createdAt/updatedAt timestamps.
Returns 404 if the current user has not favorited the Query.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/queries/00000000-0000-4000-8000-000000000001/favorite"
}
},
"permissions": {},
"id": "00000000-0000-4000-8000-000000000002",
"principalId": "admin",
"reportId": null,
"datasetId": null,
"queryId": "00000000-0000-4000-8000-000000000001",
"datasourceId": null,
"jobId": null,
"assistantId": null,
"templateId": null,
"libraryId": null,
"appId": null,
"integrationId": null,
"toolkitId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
}
PUT /api/queries/{id}/favorite
Mark a Query as a favorite for the current user.
Authentication: Required (read access to the Query)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query UUID or natural id (ownerId:slug) |
Response:
Upserts the Favorite for (tenant, principalId, queryId) and responds 200 with
the row. Idempotent: marking an already-favorited Query returns the existing row.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/queries/00000000-0000-4000-8000-000000000001/favorite"
}
},
"permissions": {},
"id": "00000000-0000-4000-8000-000000000002",
"tenant": "acme",
"queryId": "00000000-0000-4000-8000-000000000001",
"principalId": "admin",
"reportId": null,
"datasetId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"datasourceId": null,
"jobId": null,
"templateId": null,
"assistantId": null,
"libraryId": null,
"appId": null,
"integrationId": null,
"toolkitId": null
}
DELETE /api/queries/{id}/favorite
Remove the current user's favorite for a Query.
Authentication: Required (read access to the Query)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Query UUID or natural id (ownerId:slug) |
Response:
Destroys the current user's favorite for the Query and responds 200 with an
empty body, not 204. Deleting when not favorited is a no-op 200.
Tagging Best Practices
Tag Categories
Build a taxonomy of tags for different purposes:
| Category | Examples | Use Case |
|---|---|---|
| Department | Finance, Sales, Marketing, HR | Organize by department |
| Purpose | Reporting, Analysis, Export, Audit | Categorize by use case |
| Frequency | Daily, Weekly, Monthly, Quarterly | Track update cadence |
| Status | Draft, Reviewed, Production, Deprecated | Manage lifecycle |
| Data Source | MySQL, Postgres, Salesforce, API | Filter by source |
Naming Conventions
- Consistent casing keeps a tag library readable.
- Hyphens for multi-word tags:
sales-pipeline. - Singular form:
report, notreports. - Specific labels:
q4-revenue, not justrevenue. - Short names:
fy24, notfiscal-year-2024.
Multi-Tag Strategy
Assign several tags to a Query so it surfaces under different filters. Each tag is referenced by its UUID:
PUT /api/queries/{queryId}/tags/{financeTagId}
PUT /api/queries/{queryId}/tags/{quarterlyTagId}
PUT /api/queries/{queryId}/tags/{revenueTagId}
query:assignTags is distinct from write access. It lets team members organize
content without granting them the ability to edit the Query itself.
Favorites Best Practices
When to Favorite
| Scenario | Action |
|---|---|
| Frequently used Queries | Favorite for quick access |
| Monitoring Queries | Favorite to check regularly |
| Template Queries | Favorite for copying |
| Important references | Favorite for easy finding |
Keeping Favorites Useful
- Favorite the Queries you return to often.
- Tag them by category so they stay findable.
- Periodically remove favorites you no longer use, so the list stays short enough to be worth scanning.