Skip to main content

Roles

Looking for the product view?
The Help Center explains this surface as users see it: Sharing Apps.

Apps support custom roles defined in informer.yaml. Roles provide fine-grained access control beyond the standard team permission levels, letting app authors control which features and data individual users can access.

How Roles Work​

  1. Define roles in informer.yaml (shipped with the app)
  2. Assign roles to users/teams via the share endpoint
  3. Check roles in client-side code (window.__INFORMER__.roles) or server route handlers (request.roles)

Role Definition​

Define roles in your app's informer.yaml file:

# informer.yaml
roles:
- id: viewer
name: Viewer
description: Read-only access to dashboards
- id: editor
name: Editor
description: Can modify records and settings
- id: approver
name: Approver
description: Can approve submitted items

Each role requires:

FieldTypeRequiredDescription
idstringYesUnique identifier (used in code and API calls)
namestringYesHuman-readable display name
descriptionstringNoWhat this role allows

Role Resolution Rules​

When an app is viewed, Informer resolves the current user's roles:

User TypeRoles Received
SuperuserAll defined roles
Publisher+ on owning teamAll defined roles
Shared user/teamOnly roles assigned in their share record
User without shareEmpty array []
No roles defined in YAMLEmpty array [] for everyone

Publishers and above on the owning team automatically receive all roles — they don't need explicit role assignments. Regular shared users only receive the roles listed in their AppShare.roles array.


GET /api/apps/{id}/roles​

Returns the role definitions from the app's informer.yaml. This returns the available roles, not the current user's assigned roles.

Authentication: Required

Permissions Required: None (any user who can read the app)

Path Parameters:

ParameterTypeDescription
idstringApp UUID or natural ID
List an App's role definitionsGET /api/apps/admin%3Aorder-desk/roles
These are the roles available for assignment (from the manifest's roles: block), not the caller's resolved roles.
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/roles"
}
},
"roles": [
{
"id": "viewer",
"name": "Viewer",
"description": "Read-only access to dashboards"
},
{
"id": "approver",
"name": "Approver",
"description": "Can approve submitted orders"
}
]
}
Captured from the API examples test suite; ids and timestamps are normalized.

Response Fields:

FieldTypeDescription
rolesarrayList of role definitions from informer.yaml
roles[].idstringRole identifier
roles[].namestringDisplay name
roles[].descriptionstringOptional description

Error Responses:

  • 404 Not Found - App doesn't exist or user lacks access

Notes:

  • Returns an empty roles array if no informer.yaml exists or no roles are defined
  • Invalid role entries (missing id or name) are silently filtered out

Roles in Shares​

When sharing an app, you can assign roles to the share recipient. Use the roles field in the PUT /api/apps/{id}/shares/{principalId} endpoint:

PUT /api/apps/analytics:sales-dashboard/shares/john.doe
{
"accessLevel": 1,
"roles": ["viewer", "approver"]
}

The roles array contains role IDs that must match definitions in informer.yaml. Role IDs that don't match a defined role are stored but ignored at view time.

See Ownership & Sharing for the full share endpoint documentation.


Roles in View Context​

When an app is viewed, the resolved roles are injected into the browser context:

window.__INFORMER__ = {
report: { id: 'app-uuid', name: 'Sales Dashboard' },
theme: 'light',
roles: ['viewer', 'approver']
};

Use window.__INFORMER__.roles to conditionally render UI:

const roles = window.__INFORMER__?.roles || [];

if (roles.includes('approver')) {
document.getElementById('approveBtn').style.display = 'block';
}

if (roles.includes('editor')) {
enableEditMode();
}

Roles in Server Routes​

Server route handlers receive the user's roles via request.roles and can declare required roles in their config:

Checking Roles in Handler Code​

// server/dashboard.js
export async function GET({ request, query }) {
const isEditor = request.roles.includes('editor');

const rows = await query('SELECT * FROM items ORDER BY id');

return {
items: rows,
canEdit: isEditor
};
}

Restricting Routes by Role​

Use config.roles to restrict an entire route to specific roles. Users without a matching role receive 403 Forbidden:

// server/approve.js
export const config = {
roles: ['approver']
};

export async function POST({ request, query }) {
const { itemId } = request.body;
await query('UPDATE items SET status = $1 WHERE id = $2', ['approved', itemId]);
return { status: 200, body: { approved: true } };
}

The config.roles check uses OR logic — the user needs any one of the listed roles.


Common Patterns​

Role-Based UI​

const roles = window.__INFORMER__?.roles || [];

// Show/hide sections based on roles
document.getElementById('admin-panel').hidden = !roles.includes('editor');
document.getElementById('approve-section').hidden = !roles.includes('approver');

// Disable inputs for viewers
if (!roles.includes('editor')) {
document.querySelectorAll('input, select, textarea').forEach(el => {
el.disabled = true;
});
}

Share with Roles​

// Share app with a user and assign roles
await fetch('/api/apps/analytics:sales-dashboard/shares/john.doe', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
accessLevel: 1,
roles: ['viewer', 'approver']
})
});

Server Route with Role Restriction​

// server/items/[id]/approve.js
export const config = {
roles: ['approver'],
timeout: 10000
};

export async function POST({ request, query }) {
const rows = await query(
'UPDATE items SET status = $1, approved_at = NOW() WHERE id = $2 RETURNING *',
['approved', request.params.id]
);

if (rows.length === 0) {
return { status: 404, body: { error: 'Item not found' } };
}

return { status: 200, body: rows[0] };
}

POST /api/apps/{id}/_consent​

Record the current user's consent to the app's requested permissions (shown when a user first opens an app, or after its permissions change).

Authentication: Required

FieldTypeRequiredDescription
permissionsHashstringYesSHA-256 hash of the permissions being consented to. Must match the app's current hash or the request fails with 409 (the permissions changed since the page loaded — re-review them).
previousHashstringNoThe prior hash, for re-consent tracking.

Returns { "consented": true }.

note

The current permissions hash is computed server-side from the app's manifest; there is no captured example here because the hash is environment-specific.