Skip to content

Roles

Roles connect workflows to people: an escalation targets a role, and users who hold that role see it in their queue. This page covers role assignment on users, role configuration under /api/roles, and escalation chains.

Roles are implicit: a role exists the moment you reference it. Role assignment endpoints are nested under /api/users/:id/roles. All endpoints require authentication.

Every role assignment has a type that controls management permissions.

Type Permissions
member Work the role’s task queue at the breadth set by its work-surface scope (see below)
admin Everything a member can do on the whole queue, plus manage users within this role
superadmin Full access: manage all roles, all users, system configuration

A user can hold multiple roles with different types. For example, a user might be a member of reviewer and an admin of senior-reviewer.

A role is a task queue with four verbs: search, claim, ack (resolve) and delete (cancel). A member carries two independent scope axes that set the breadth of those verbs. admin and superadmin ignore scope and always act on the whole queue.

Axis Values Governs
read_scope self, all search: which escalations the member sees
write_scope none, self, all claim / ack / delete: which escalations the member may act on

self means escalations assigned to the member (assigned_to = user); all means the whole role queue. The only constraint is write ⊆ read (you cannot act on what you cannot see), so write_scope=all requires read_scope=all. Both default to all, which is the full-queue worker.

The five member profiles:

read_scope write_scope Profile
all all Full worker: search and act on the whole queue (default)
all self See the whole queue, act only on own items (for example, a chat-style room)
self self Own items only: a one-time user filling in their pre-assigned form
all none Read-only observer or auditor of the queue
self none Read-only view of one’s own items

Releasing and escalating are queue-management verbs (they move an item out of the member’s hands) and require write_scope=all. Creating a standalone escalation also requires write_scope=all. A member with write_scope=self may only claim, ack or delete items already assigned to them.

This makes one-time and limited-surface users first-class. A workflow can assign an escalation to a named person (pre-claim) and provision them as read_self + write_self. They see and act on exactly that one item: the form the workflow routed to them.

Three role names have fixed types and elevated permissions:

Role Fixed type Dashboard Escalation scope User management
superadmin superadmin Full (builder + admin) All roles; role filter ignored Create other superadmins. Manage any user, any role
engineer admin Full (builder) Engineer role only Assign users to engineer role
admin admin Escalations + user management All roles; role filter ignored Assign users to any non-special role. Bulk actions on all escalations

All other role names are dynamic. A dynamic role can have type member (work the queue at its read and write scope) or admin (whole-queue access plus managing users within that role and bulk actions).

The dashboard adapts to the authenticated user’s access tier:

Tier Condition Sees
Builder superadmin or engineer role Full dashboard: workflows, pipelines, MCP, design, storage, admin, all home page sections
Operations Any role with admin type Home page (escalations only), escalation pages, user and role management (scoped to their roles)
Operator Any role with member type Home page (escalations only), escalation pages, scoped to the member’s read scope

Bulk escalation actions (bulk claim, assign, triage, escalate) require admin or superadmin type. Plain member users work on escalations one at a time. A member whose read scope is self lands directly on their own item in user mode; the queue list and aggregate stats reflect only read_all memberships.

Certain API endpoints require builder access (superadmin or engineer role):

Endpoint Access
POST/PUT/PATCH/DELETE /api/users Builder
POST /api/users/:id/roles Admin (scoped, see below)
DELETE /api/users/:id/roles/:role Admin (scoped, see below)
All /api/bot-accounts Builder
All /api/roles mutations Admin access or the engineer role
All /api/controlplane Builder

When assigning roles via POST /api/users/:id/roles, the caller’s own roles determine what they can assign:

Caller Can assign
superadmin Any role, any type (including superadmin/superadmin)
engineer Any role up to admin type (never superadmin type)
*/admin (non-builder) member or admin type for roles they themselves hold

Removing a role follows the same rule for the role at the type the account holds it. Only a superadmin may change, remove roles from, or mint keys for an account that holds a superadmin grant; others receive 403.

A caller who may assign a role may set any work-surface scope (read_scope, write_scope) on it. Scope refines a member grant; admin and superadmin always act on the whole queue.

GET /api/users/:id/roles

Returns every role the user holds, with its type.

Path parameters

Parameter Description
id User UUID

Example request

Terminal window
curl https://lt.acme.dev/api/users/b2c3d4e5-.../roles \
-H "Authorization: Bearer $LT_TOKEN"

Response 200

{
"roles": [
{
"user_id": "b2c3d4e5-...",
"role": "reviewer",
"type": "member",
"created_at": "2025-01-10T08:00:00.000Z"
},
{
"user_id": "b2c3d4e5-...",
"role": "senior-reviewer",
"type": "admin",
"created_at": "2025-01-10T08:00:00.000Z"
}
]
}
POST /api/users/:id/roles

Grants a role to a user at a type and, for a member, a work-surface scope. A user holds each role at most once (the primary key is (user_id, role)), so adding a role the user already holds returns an error.

Scope: read_scope and write_scope set the member’s breadth on the role’s queue; write_scope=all requires read_scope=all.

Auth: Admin access, following the scoped role assignment rules.

Path parameters

Parameter Description
id User UUID

Request body

Field Type Required Description
role string yes Role name (for example, reviewer)
type string yes superadmin, admin, or member
read_scope string no self or all (default all). Search breadth for a member; ignored for admin and superadmin
write_scope string no none, self, or all (default all). Claim, ack and delete breadth for a member

Example request (a one-time user who works only their own assigned item)

Terminal window
curl -X POST https://lt.acme.dev/api/users/b2c3d4e5-.../roles \
-H "Authorization: Bearer $LT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "role": "customer-triage", "type": "member", "read_scope": "self", "write_scope": "self" }'

Response 201

{
"user_id": "b2c3d4e5-...",
"role": "customer-triage",
"type": "member",
"read_scope": "self",
"write_scope": "self",
"created_at": "2025-01-15T12:00:00.000Z"
}

Response 400

{ "error": "role and type are required" }
{ "error": "type must be superadmin, admin, or member" }
{ "error": "write_scope=all requires read_scope=all (cannot act on what you cannot see)" }
DELETE /api/users/:id/roles/:role

Removes one role from a user.

Auth: Admin access, following the scoped role assignment rules.

Path parameters

Parameter Description
id User UUID
role Role name to remove

Example request

Terminal window
curl -X DELETE https://lt.acme.dev/api/users/b2c3d4e5-.../roles/reviewer \
-H "Authorization: Bearer $LT_TOKEN"

Response 200

{ "removed": true }

Response 404

{ "error": "Role not found" }

The endpoints below manage roles as standalone entities under /api/roles. All require authentication. Mutations require a role manager: admin type, superadmin, or the engineer role.

GET /api/roles

Returns all distinct role names known to the system.

Example request

Terminal window
curl https://lt.acme.dev/api/roles \
-H "Authorization: Bearer $LT_TOKEN"

Response 200

{
"roles": ["reviewer", "senior-reviewer", "admin"]
}
GET /api/roles/details

Returns all roles with their configuration and usage counts.

Example request

Terminal window
curl https://lt.acme.dev/api/roles/details \
-H "Authorization: Bearer $LT_TOKEN"

Response 200

{
"roles": [
{
"role": "reviewer",
"title": "Document Reviewer",
"description": "Reviews flagged documents for compliance",
"form_schema": null,
"metadata_schema": null,
"properties": {},
"ops_visible": true,
"parent_role": null,
"sla_minutes": 30,
"target_per_hour": 20,
"worker_count": 4,
"priority_threshold_minutes": null,
"priority_facet": null,
"entity_facet": null,
"entity_state_source": "role",
"user_count": 5,
"chain_count": 2,
"workflow_count": 3
}
]
}
Field Type Description
title string | null Display name separate from the technical role key
description string | null Human-readable summary
form_schema object | null JSON Schema for the escalation resolve form. Versioned per role; fields may carry x-lt-bind to map values into the payload. Takes precedence over the deprecated workflow-level resolver_schema
metadata_schema object | null JSON Schema declaring the expected shape of lt_escalations.metadata for this role. Drives faceted-query key autocomplete and creation-time validation
properties object Free user-owned bag: icons, colors, tags and so on. No reserved keys
ops_visible boolean When true, the role appears as a station on the /operations view
enforce_schema boolean When true, every resolve surface validates submitted payloads against this role’s form schema and rejects violations with 422 (code: schema_validation). See Schema enforcement
parent_role string | null Parent role in the process dependency graph; null for root stations
sla_minutes number | null Target resolution time in minutes (capacity setting)
target_per_hour number | null Intended throughput: items resolved per hour (capacity setting)
worker_count number | null Capacity at this station: staff or machine count (capacity setting)
priority_threshold_minutes number | null Max age (minutes) before a pending unclaimed item counts toward priority_count in station metrics; falls back to sla_minutes
priority_facet string | null Escalation metadata key holding the age origin as an ISO 8601 UTC timestamp (for example, authorized_at); falls back to created_at
entity_facet string | null Escalation metadata key naming the entity that moves through this role (for example, serialNumber, orderId). Roles sharing the key form that entity’s system; the analytics surfaces (distinct-entity counts, per-entity dwell, entity timelines) derive from it. null means the role has no entity notion
entity_state_source 'role' | 'subtype' How the role names its contribution to the entity’s state space: 'role' (the station itself is the state) or 'subtype' (the role’s subtypes are its states). Default 'role'; only meaningful while entity_facet is set
user_count number Number of users assigned this role
chain_count number Number of escalation chain entries referencing this role
workflow_count number Number of workflow configs that reference this role
POST /api/roles

Creates a standalone role.

Auth: Role manager (admin type, superadmin, or engineer).

Request body

Field Type Required Description
role string yes Role name (lowercase letters, numbers, hyphens, underscores; must start with a letter)

Example request

Terminal window
curl -X POST https://lt.acme.dev/api/roles \
-H "Authorization: Bearer $LT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "role": "senior-reviewer" }'

Response 201

{ "role": "senior-reviewer" }

Response 400

{ "error": "role is required" }
{ "error": "Role must start with a letter and contain only lowercase letters, numbers, hyphens, and underscores" }
DELETE /api/roles/:role

Deletes a role when nothing references it.

Auth: Role manager (admin type, superadmin, or engineer).

Path parameters

Parameter Description
role Role name to delete

Example request

Terminal window
curl -X DELETE https://lt.acme.dev/api/roles/senior-reviewer \
-H "Authorization: Bearer $LT_TOKEN"

Response 200

{ "deleted": true }

Response 409

{ "error": "Cannot delete role" }
PATCH /api/roles/:role

Updates role configuration. Only provided fields change; form_schema, metadata_schema and parent_role accept null to clear them.

When the update changes form_schema or metadata_schema, the new pair is snapshotted into the role’s schema version history and current_schema_version advances. A save with identical schema values leaves the version alone. Pass change_summary to label the snapshot.

Auth: Role manager (admin type, superadmin, or engineer).

Path parameters

Parameter Description
role Role key to update

Request body (all fields optional)

Field Type Description
title string | null Display name
description string | null Short description
form_schema object | null JSON Schema for the resolve form
metadata_schema object | null JSON Schema for lt_escalations.metadata
properties object | null Free user-owned bag
ops_visible boolean Include in the /operations view
enforce_schema boolean Turn server-side resolver schema validation on or off
parent_role string | null Parent in the process dependency graph
sla_minutes number | null SLA target in minutes
target_per_hour number | null Throughput target (items per hour)
worker_count number | null Station capacity
priority_threshold_minutes number | null Priority age threshold in minutes; falls back to sla_minutes
priority_facet string | null Metadata key for the priority age origin (letters, numbers, underscores); falls back to created_at
entity_facet string | null Metadata key naming the entity that moves through this role (letters, numbers, underscores). Roles sharing the key form the entity’s system for the analytics surfaces. null clears
entity_state_source 'role' | 'subtype' | null How the role names the entity’s states: the station itself ('role') or its subtypes ('subtype'). null resets to the 'role' default
upstream_roles string[] | null Replace the set of roles this station draws input from across other Operations sequences (omitted preserves; null or [] clears). Every entry must name an existing role other than this one. Distinct from parent_role, which places the role in its own sequence
list_schema object | null Rich formatting (x-lt-* markup) for this role’s escalation list page. Versions independently of form_schema; the list always renders the latest
default_pins array | null Pinned-view seeds for members: [{ label, url, badge? }] with dashboard-relative urls. Members promote, hide or reorder them through their own preferences (/api/me/preferences). null clears
portals array | null The role’s named portals: [{ key, label, rows, counts? }]. key is a slug unique within the role, rows holds 1 to 4 rows of 1 to 6 pin cells { label, url, badge? }, and counts holds up to 8 tiles { label, url, blurb? } shown above the panels. Each portal renders as one page at /portal/<role>/<key>. Up to 12. null clears
change_summary string Label recorded on the schema version snapshot when this update changes a schema field

Example request (configure a role as a station in the operations view)

Terminal window
curl -X PATCH https://lt.acme.dev/api/roles/reviewer \
-H "Authorization: Bearer $LT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Document Reviewer",
"ops_visible": true,
"sla_minutes": 30,
"target_per_hour": 20,
"worker_count": 4
}'

Response 200

The updated RoleDetail object (same shape as GET /api/roles/details).

Response 404

{ "error": "Role 'unknown-role' not found" }
GET /api/roles/:role/schema

Fetches the role’s form_schema and metadata_schema pair. Without version, it returns the live (latest) schema with the role’s current version number; with version, it returns that immutable snapshot, the one an escalation pinned through metadata.schema_version (the schemaVersion field of conditional).

Path parameters

Parameter Description
role Role name

Query parameters

Parameter Type Description
version integer Snapshot version to fetch. Omit for the latest

Example request

Terminal window
curl "https://lt.acme.dev/api/roles/reviewer/schema?version=3" \
-H "Authorization: Bearer $LT_TOKEN"

Response 200

{
"role": "reviewer",
"version": 3,
"form_schema": { "type": "object", "properties": { "approved": { "type": "boolean" } } },
"metadata_schema": { "type": "object", "properties": { "order_id": { "type": "string" } } },
"change_summary": "Added lotNumber field",
"created_at": "2026-07-01T12:00:00.000Z",
"latest_version": 4
}

Response 404

The role does not exist, or the requested version does not exist for it. A missing version is an error; the endpoint returns exactly the version requested.

GET /api/roles/:role/schema/versions

Lists the role’s schema version history, newest first. Each entry carries presence flags in place of the schemas; fetch a full snapshot with GET /api/roles/:role/schema?version=N.

Path parameters

Parameter Description
role Role name

Example request

Terminal window
curl https://lt.acme.dev/api/roles/reviewer/schema/versions \
-H "Authorization: Bearer $LT_TOKEN"

Response 200

{
"versions": [
{
"version": 4,
"has_form_schema": true,
"has_metadata_schema": true,
"change_summary": null,
"created_at": "2026-07-02T09:30:00.000Z",
"is_current": true
},
{
"version": 3,
"has_form_schema": true,
"has_metadata_schema": true,
"change_summary": "Added lotNumber field",
"created_at": "2026-07-01T12:00:00.000Z",
"is_current": false
}
]
}

Escalation chains define which roles can escalate to which other roles. They live under /api/roles/escalation-chains, and the per-role view is /api/roles/:role/escalation-targets.

GET /api/roles/escalation-chains

Returns all escalation chain pairs.

Example request

Terminal window
curl https://lt.acme.dev/api/roles/escalation-chains \
-H "Authorization: Bearer $LT_TOKEN"

Response 200

{
"chains": [
{ "source_role": "reviewer", "target_role": "senior-reviewer" }
]
}
POST /api/roles/escalation-chains

Adds a single escalation chain entry.

Auth: Role manager (admin type, superadmin, or engineer).

Request body

Field Type Required Description
source_role string yes Role that can escalate
target_role string yes Role to escalate to

Example request

Terminal window
curl -X POST https://lt.acme.dev/api/roles/escalation-chains \
-H "Authorization: Bearer $LT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "source_role": "reviewer", "target_role": "senior-reviewer" }'

Response 201

{ "source_role": "reviewer", "target_role": "senior-reviewer" }

Response 400

{ "error": "source_role and target_role are required" }
DELETE /api/roles/escalation-chains

Removes a single escalation chain entry.

Auth: Role manager (admin type, superadmin, or engineer).

Request body

Field Type Required Description
source_role string yes Source role
target_role string yes Target role

Example request

Terminal window
curl -X DELETE https://lt.acme.dev/api/roles/escalation-chains \
-H "Authorization: Bearer $LT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "source_role": "reviewer", "target_role": "senior-reviewer" }'

Response 200

{ "removed": true }

Response 404

{ "error": "Chain entry not found" }
GET /api/roles/:role/escalation-targets

Returns the allowed escalation targets for one role.

Path parameters

Parameter Description
role Role name

Example request

Terminal window
curl https://lt.acme.dev/api/roles/reviewer/escalation-targets \
-H "Authorization: Bearer $LT_TOKEN"

Response 200

{
"targets": ["senior-reviewer", "manager"]
}
PUT /api/roles/:role/escalation-targets

Replaces all escalation targets for a role.

Auth: Role manager (admin type, superadmin, or engineer).

Path parameters

Parameter Description
role Role name

Request body

Field Type Required Description
targets string[] yes New list of target roles

Example request

Terminal window
curl -X PUT https://lt.acme.dev/api/roles/reviewer/escalation-targets \
-H "Authorization: Bearer $LT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "targets": ["senior-reviewer", "manager"] }'

Response 200

{ "role": "reviewer", "targets": ["senior-reviewer", "manager"] }

Response 400

{ "error": "targets must be an array of strings" }

Roles also appear in workflow configuration. When you register a workflow, default_role sets the escalation target and roles lists every role allowed to claim its escalations (see Create or replace a workflow configuration):

Terminal window
curl -s https://lt.acme.dev/api/workflows/reviewContent/config \
-X PUT \
-H "Authorization: Bearer $LT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"is_lt": true, "default_role": "reviewer", "roles": ["reviewer", "senior-reviewer"]}'

When reviewContent escalates, the escalation targets reviewer by default. Users with either reviewer or senior-reviewer can claim it.

See the Workflows API for full configuration details.