Tags
Apps can be tagged for organization and filtering. Tags are managed through the TagEntity association.
GET /api/apps/{id}/tags
Get all tags assigned to an app.
Authentication: Required
Permissions Required: None (any user who can read the app)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | App UUID or natural ID |
Response:
Returns a HAL collection of TagEntity records with embedded Tag details.
{
"_links": {
"self": { "href": "/api/apps/analytics:sales-dashboard/tags" }
},
"_embedded": {
"inf:tag-entity": [
{
"_links": {
"self": { "href": "/api/apps/analytics:sales-dashboard/tags/tag-uuid-1" },
"inf:tag": { "href": "/api/tags/tag-uuid-1" }
},
"id": "te-uuid-1",
"appId": "d4f8a2b1-1234-5678-90ab-cdef12345678",
"tagId": "tag-uuid-1",
"_embedded": {
"inf:tag": {
"id": "tag-uuid-1",
"name": "Sales",
"color": "blue",
"createdAt": "2023-01-15T10:00:00.000Z"
}
}
},
{
"_links": {
"self": { "href": "/api/apps/analytics:sales-dashboard/tags/tag-uuid-2" },
"inf:tag": { "href": "/api/tags/tag-uuid-2" }
},
"id": "te-uuid-2",
"appId": "d4f8a2b1-1234-5678-90ab-cdef12345678",
"tagId": "tag-uuid-2",
"_embedded": {
"inf:tag": {
"id": "tag-uuid-2",
"name": "Dashboard",
"color": "green",
"createdAt": "2023-01-15T10:05:00.000Z"
}
}
}
]
},
"items": [...]
}
Key Fields:
| Field | Description |
|---|---|
appId | App UUID |
tagId | Tag UUID |
_embedded.inf:tag | Full tag details (name, color, etc.) |
Error Responses:
404 Not Found- App doesn't exist or user lacks access
GET /api/apps/{id}/tags/{tagId}
Get a specific tag assignment.
Authentication: Required
Permissions Required: None (any user who can read the app)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | App UUID or natural ID |
tagId | string | Tag UUID |
Response:
{
"_links": {
"self": { "href": "/api/apps/analytics:sales-dashboard/tags/tag-uuid-1" },
"inf:app": { "href": "/api/apps/analytics:sales-dashboard" },
"inf:tag": { "href": "/api/tags/tag-uuid-1" }
},
"id": "te-uuid-1",
"appId": "d4f8a2b1-1234-5678-90ab-cdef12345678",
"tagId": "tag-uuid-1",
"createdAt": "2024-02-10T10:00:00.000Z",
"updatedAt": "2024-02-10T10:00:00.000Z"
}
Error Responses:
404 Not Found- App, tag, or assignment doesn't exist
PUT /api/apps/{id}/tags/{tagId}
Assign a tag to an app (create a TagEntity).
Authentication: Required
Permissions Required: Member+ role (assignTags permission)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | App UUID or natural ID |
tagId | string | Tag UUID |
Payload:
No payload required (or empty object).
Response:
Returns the created/existing TagEntity with status 200 OK or 201 Created.
{
"id": "te-uuid-1",
"appId": "d4f8a2b1-1234-5678-90ab-cdef12345678",
"tagId": "tag-uuid-1",
"createdAt": "2024-02-13T10:30:00.000Z",
"updatedAt": "2024-02-13T10:30:00.000Z"
}
Upsert Behavior:
- If the tag is already assigned, returns the existing TagEntity
- If not assigned, creates a new TagEntity
- Idempotent operation
Error Responses:
404 Not Found- App or tag doesn't exist, or user lacks access403 Forbidden- User lacks assignTags permission
DELETE /api/apps/{id}/tags/{tagId}
Remove a tag assignment from an app.
Authentication: Required
Permissions Required: Member+ role (assignTags permission)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | App UUID or natural ID |
tagId | string | Tag UUID |
Response:
Returns 204 No Content on success.
Error Responses:
404 Not Found- App, tag, or assignment doesn't exist403 Forbidden- User lacks assignTags permission
Common Tag Operations
Assign Multiple Tags
const tagIds = ['tag-uuid-1', 'tag-uuid-2', 'tag-uuid-3'];
for (const tagId of tagIds) {
await PUT(`/api/apps/analytics:sales-dashboard/tags/${tagId}`);
}
Get App's Tags
const response = await GET('/api/apps/analytics:sales-dashboard/tags');
const tagNames = response.items.map(item => item._embedded['inf:tag'].name);
console.log('Tags:', tagNames.join(', '));
Remove All Tags
// Get current tags
const response = await GET('/api/apps/analytics:sales-dashboard/tags');
// Remove each one
for (const item of response.items) {
await DELETE(`/api/apps/analytics:sales-dashboard/tags/${item.tagId}`);
}
Replace Tags
const newTagIds = ['tag-uuid-4', 'tag-uuid-5'];
// Get current tags
const current = await GET('/api/apps/analytics:sales-dashboard/tags');
// Remove tags not in new set
for (const item of current.items) {
if (!newTagIds.includes(item.tagId)) {
await DELETE(`/api/apps/analytics:sales-dashboard/tags/${item.tagId}`);
}
}
// Add new tags
for (const tagId of newTagIds) {
await PUT(`/api/apps/analytics:sales-dashboard/tags/${tagId}`);
}
Check if App Has Tag
try {
await GET('/api/apps/analytics:sales-dashboard/tags/tag-uuid-1');
console.log('App has this tag');
} catch (e) {
if (e.statusCode === 404) {
console.log('App does not have this tag');
}
}
Tag Permissions
The assignTags permission requires Member+ role (same as write permission). This means:
- Members can view tags but not assign/remove them
- Member+ and above can modify tag assignments
- Superusers can always modify tags
Integration with App List
Tags are included in the /api/apps-list response:
{
"id": "d4f8a2b1-...",
"name": "Sales Dashboard",
"tags": ["tag-uuid-1", "tag-uuid-2"],
...
}
And in the single app response (GET /api/apps/{id}):
{
"id": "d4f8a2b1-...",
"name": "Sales Dashboard",
"tags": ["tag-uuid-1", "tag-uuid-2"],
...
}
This allows clients to filter and organize apps by tags without making additional requests.