Skip to main content

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.

Tags are referenced by UUID

{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:

ParameterTypeDescription
idstringQuery 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.

List a query’s tagsGET /api/queries/admin:regional-sales/tags
GET /api/queries/{id}/tags is a HAL collection (rel inf:query-tags) embedding inf:tag-entity join rows (each carrying queryId/tagId), and each join row embeds inf:tag. {id} accepts the UUID or the natural id (ownerId:slug). No permission check beyond read access to the query. Tags are referenced by UUID, not by a slug.
Response · 200
{
"_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"
}
}
}
]
}
}
Captured from the API examples test suite; ids and timestamps are normalized.

GET /api/queries/{id}/tags/{tagId}

Get a single tag assignment for a Query.

Authentication: Required (read access to the Query)

Path Parameters:

ParameterTypeDescription
idstringQuery UUID or natural id (ownerId:slug)
tagIdstringTag 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.

Get a single tag assignmentGET /api/queries/00000000-0000-4000-8000-000000000001/tags/00000000-0000-4000-8000-000000000002
GET /api/queries/{id}/tags/{tagId} returns the single TagEntity join row (db.lookup) for that query+tag, carrying inf:query and inf:tag links. {tagId} is the tag UUID. Returns 404 if the tag is not assigned to the query.
Response · 200
{
"_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"
}
Captured from the API examples test suite; ids and timestamps are normalized.

PUT /api/queries/{id}/tags/{tagId}

Assign a tag to a Query.

Authentication: Required

Permission: query:assignTags

Path Parameters:

ParameterTypeDescription
idstringQuery UUID or natural id (ownerId:slug)
tagIdstringTag 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.

Assign a tag to a queryPUT /api/queries/00000000-0000-4000-8000-000000000001/tags/00000000-0000-4000-8000-000000000002
PUT /api/queries/{id}/tags/{tagId} requires permission.query.assignTags (the caller must be a Designer of the query; this is separate from write). It db.upserts the TagEntity join row and returns it with 200 (NOT 201). {tagId} is the tag UUID. There is no application-level pre-check that the tag exists, but tagId is a foreign key to tag.id, so an unknown UUID fails at the database.
Response · 200
{
"_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
}
Captured from the API examples test suite; ids and timestamps are normalized.
The tag must already exist

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:

ParameterTypeDescription
idstringQuery UUID or natural id (ownerId:slug)
tagIdstringTag 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.

Remove a tag from a queryDELETE /api/queries/00000000-0000-4000-8000-000000000001/tags/00000000-0000-4000-8000-000000000002
DELETE /api/queries/{id}/tags/{tagId} requires permission.query.assignTags. It db.removes the TagEntity join row and returns 200 with an EMPTY body (Informer convention via h.continue), NOT 204. Removing a tag that is not assigned is a no-op 200.
Response · 200 · no body
Captured from the API examples test suite; ids and timestamps are normalized.

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:

ParameterTypeDescription
idstringQuery 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.

Check favorite statusGET /api/queries/00000000-0000-4000-8000-000000000001/favorite
GET /api/queries/{id}/favorite returns the current user’s Favorite row for this query (raw row: id, principalId, queryId, plus the other nullable entity FKs and timestamps). The favorite is scoped to the CURRENT user via principalId derived from auth (req.auth.credentials.username), not client-supplied. Returns 404 if the current user has not favorited the query. There is no `userId`/`createdAt`-only shape as some docs suggest.
Response · 200
{
"_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"
}
Captured from the API examples test suite; ids and timestamps are normalized.

PUT /api/queries/{id}/favorite

Mark a Query as a favorite for the current user.

Authentication: Required (read access to the Query)

Path Parameters:

ParameterTypeDescription
idstringQuery 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.

Mark a query as favoritePUT /api/queries/00000000-0000-4000-8000-000000000001/favorite
PUT /api/queries/{id}/favorite db.upserts the Favorite for (tenant, principalId, queryId) where principalId is the current user (from auth), and returns the row with 200. Idempotent: PUTting an already-favorited query returns the existing row. No permission check beyond read access to the query.
Response · 200
{
"_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
}
Captured from the API examples test suite; ids and timestamps are normalized.

DELETE /api/queries/{id}/favorite

Remove the current user's favorite for a Query.

Authentication: Required (read access to the Query)

Path Parameters:

ParameterTypeDescription
idstringQuery 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.

Remove a query from favoritesDELETE /api/queries/00000000-0000-4000-8000-000000000001/favorite
DELETE /api/queries/{id}/favorite destroys the current user’s favorite for the query and returns 200 with an EMPTY body (h.continue), NOT 204. Deleting when not favorited is a no-op 200. The favorite removed is always the current user’s (principalId from auth).
Response · 200 · no body
Captured from the API examples test suite; ids and timestamps are normalized.

Tagging Best Practices

Tag Categories

Build a taxonomy of tags for different purposes:

CategoryExamplesUse Case
DepartmentFinance, Sales, Marketing, HROrganize by department
PurposeReporting, Analysis, Export, AuditCategorize by use case
FrequencyDaily, Weekly, Monthly, QuarterlyTrack update cadence
StatusDraft, Reviewed, Production, DeprecatedManage lifecycle
Data SourceMySQL, Postgres, Salesforce, APIFilter by source

Naming Conventions

  1. Consistent casing keeps a tag library readable.
  2. Hyphens for multi-word tags: sales-pipeline.
  3. Singular form: report, not reports.
  4. Specific labels: q4-revenue, not just revenue.
  5. Short names: fy24, not fiscal-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}
assignTags is a separate permission

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

ScenarioAction
Frequently used QueriesFavorite for quick access
Monitoring QueriesFavorite to check regularly
Template QueriesFavorite for copying
Important referencesFavorite for easy finding

Keeping Favorites Useful

  1. Favorite the Queries you return to often.
  2. Tag them by category so they stay findable.
  3. Periodically remove favorites you no longer use, so the list stays short enough to be worth scanning.