Skip to main content

Core CRUD

Endpoints for managing Assistants: the paged collection, the legacy flat list, single-Assistant read, create, update, and delete, the skill-template catalog, and per-Assistant usage statistics.

Every route on this page is gated by AI access (ai.verify): AI must be enabled on the tenant AND the caller must have AI access. Read access to individual Assistants is enforced by the read_access model scope rather than a permission, so list and lookup routes return only the Assistants the caller may see. Writes additionally require the matching permission.

Most routes accept either the raw Assistant id (a UUID) or the compound natural id ownerId:slug in the {id} path segment.

GET /api/assistants

List Assistants as a paged HAL collection, sorted by name. Only published Assistants are returned (rows where editingId is null); drafts are excluded.

Authentication: Required (AI access via ai.verify)

Response:

A HAL collection embedding inf:assistant rows under _embedded. The envelope carries the HAL paging fields start, count, and total. Each row eager-loads its skills association (empty here), its inlined model (with the model's provider, key stripped), a permissions map, and the full set of inf:assistant-* rels. The query is scoped to read_access.

List assistantsGET /api/assistants
GET /api/assistants is a HAL collection (rel inf:assistant) of published assistants only (editingId is null), scoped to read_access and sorted by name. Each row eager-loads its `skills` association. Requires AI access (ai.verify).
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants"
}
},
"start": 0,
"count": 2,
"total": 2,
"_embedded": {
"inf:assistant": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/00000000-0000-4000-8000-000000000001"
},
"inf:draft": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/draft"
},
"inf:edit": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/_edit"
},
"inf:comments": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/comments{?sort,limit}",
"templated": true
},
"inf:assistant-share": {
"href": "https://informer.example.com/api/assistants/00000000-0000-4000-8000-000000000001/shares/{principalId}",
"templated": true
},
"inf:assistant-owner": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/owner"
},
"inf:assistant-datasets": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/datasets"
},
"inf:assistant-skills": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/skills"
},
"inf:assistant-toolkits": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/toolkits"
},
"inf:assistant-references": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/references"
},
"inf:assistant-shares": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/shares"
},
"inf:assistant-icon": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/icon"
},
"inf:assistant-secrets": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/secrets"
},
"inf:assistant-copy": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/_copy"
},
"inf:favorite": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/favorite"
},
"inf:assistant-files": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/files"
},
"inf:assistant-file-upload": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/files/_upload"
},
"inf:assistant-usage": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/usage"
},
"inf:create-image": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/_create-image"
},
"inf:chat-external": {
"href": "https://informer.example.com/assistants/admin%3Asales-assistant/chat"
}
},
"naturalId": "admin:sales-assistant",
"searchName": "sales assistant",
"permissions": {
"chat": true,
"edit": true,
"share": true,
"delete": true,
"write": true,
"changeOwner": true,
"copy": true,
"rename": true,
"assignTags": true
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "Sales Assistant",
"description": "Helps with sales data analysis",
"slug": "sales-assistant",
"ownerId": "admin",
"welcome": "Hello! How can I help with sales today?",
"modelId": "00000000-0000-4000-8000-000000000002",
"instructions": "You are a helpful sales analyst.",
"suggestions": [
"Show me this month's top deals",
"Analyze quarterly trends"
],
"token": null,
"data": null,
"source": null,
"sourceId": null,
"folderId": null,
"editingId": null,
"shared": true,
"embedded": false,
"dispatcher": false,
"libraryId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"skills": [],
"model": {
"naturalId": "acme-custom-opus",
"permissions": {},
"promptPpm": null,
"completionPpm": null,
"cacheReadPpm": null,
"cacheWriteShortPpm": null,
"cacheWriteLongPpm": null,
"id": "00000000-0000-4000-8000-000000000002",
"model": "claude-opus-4",
"slug": "acme-custom-opus",
"provider": {
"permissions": {},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000003",
"name": "Anthropic",
"slug": "anthropic-1",
"type": "anthropic",
"key": null,
"managed": true,
"internal": false,
"settings": null,
"data": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
},
"icon": null
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/00000000-0000-4000-8000-000000000004"
},
"inf:draft": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/draft"
},
"inf:edit": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/_edit"
},
"inf:comments": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/comments{?sort,limit}",
"templated": true
},
"inf:assistant-share": {
"href": "https://informer.example.com/api/assistants/00000000-0000-4000-8000-000000000004/shares/{principalId}",
"templated": true
},
"inf:assistant-owner": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/owner"
},
"inf:assistant-datasets": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/datasets"
},
"inf:assistant-skills": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/skills"
},
"inf:assistant-toolkits": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/toolkits"
},
"inf:assistant-references": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/references"
},
"inf:assistant-shares": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/shares"
},
"inf:assistant-icon": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/icon"
},
"inf:assistant-secrets": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/secrets"
},
"inf:assistant-copy": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/_copy"
},
"inf:favorite": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/favorite"
},
"inf:assistant-files": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/files"
},
"inf:assistant-file-upload": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/files/_upload"
},
"inf:assistant-usage": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/usage"
},
"inf:create-image": {
"href": "https://informer.example.com/api/assistants/admin%3Ascratch-assistant/_create-image"
},
"inf:chat-external": {
"href": "https://informer.example.com/assistants/admin%3Ascratch-assistant/chat"
}
},
"naturalId": "admin:scratch-assistant",
"searchName": "scratch assistant",
"permissions": {
"chat": true,
"edit": true,
"share": true,
"delete": true,
"write": true,
"changeOwner": true,
"copy": true,
"rename": true,
"assignTags": true
},
"id": "00000000-0000-4000-8000-000000000004",
"name": "Scratch Assistant",
"description": "Temporary assistant for the delete example",
"slug": "scratch-assistant",
"ownerId": "admin",
"welcome": null,
"modelId": "00000000-0000-4000-8000-000000000002",
"instructions": null,
"suggestions": null,
"token": null,
"data": null,
"source": null,
"sourceId": null,
"folderId": null,
"editingId": null,
"shared": false,
"embedded": false,
"dispatcher": false,
"libraryId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"skills": [],
"model": {
"naturalId": "acme-custom-opus",
"permissions": {},
"promptPpm": null,
"completionPpm": null,
"cacheReadPpm": null,
"cacheWriteShortPpm": null,
"cacheWriteLongPpm": null,
"id": "00000000-0000-4000-8000-000000000002",
"model": "claude-opus-4",
"slug": "acme-custom-opus",
"provider": {
"permissions": {},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000003",
"name": "Anthropic",
"slug": "anthropic-1",
"type": "anthropic",
"key": null,
"managed": true,
"internal": false,
"settings": null,
"data": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
},
"icon": null
}
]
}
}
Captured from the API examples test suite; ids and timestamps are normalized.

Assistant properties:

FieldTypeDescription
idstring (UUID)Assistant id
naturalIdstringCompound natural id (ownerId:slug)
namestringDisplay name
slugstringURL-safe name
descriptionstring | nullDescription
instructionsstring | nullSystem instructions for the Assistant
welcomestring | nullWelcome message shown to users
suggestionsarray | nullSuggested prompts
dispatcherbooleanWhether this is a dispatcher Assistant
modelIdstring (UUID)Preferred model id
modelobjectEager-loaded model (provider inlined, key stripped)
tokenstring | nullEncrypted at rest; never returned raw
sharedbooleanWhether the Assistant is team-shared
embeddedbooleanInternally-created (library/App) Assistant
dataobject | nullCustom metadata
ownerIdstringOwning principal (server-derived)
folderIdstring | nullFolder id
editingIdstring | nullDraft pointer (null for published rows)
iconstring | nullIcon identifier
skillsarrayEager-loaded skill associations
permissionsobjectCaller's semantic permissions on this Assistant
createdAt / updatedAtdateTimestamps

GET /api/assistants-list

The legacy flat-list endpoint: a bare array (not a HAL collection, no paging envelope) built from a read-access SQL projection, then decorated per row with tags and sharing. Like the paged route, it excludes drafts (editingId is null) and embedded Assistants.

Authentication: Required (AI access via ai.verify)

Response:

A flat array. Each row is a lightweight projection that carries naturalId, permissions, ownerName, favorite, iconId, suggestions, and tags, rather than the full Assistant columns. sharing is included when present.

List assistants (legacy flat list)GET /api/assistants-list
GET /api/assistants-list is the legacy endpoint: a flat array built from a read-access SQL projection, decorated per row with `tags` and `sharing`. Excludes drafts and embedded assistants. Each row carries naturalId/permissions/ownerName/favorite rather than the full assistant columns. Requires AI access (ai.verify).
Response · 200
[
{
"id": "00000000-0000-4000-8000-000000000001",
"name": "Sales Assistant",
"description": "Helps with sales data analysis",
"suggestions": [
"Show me this month's top deals",
"Analyze quarterly trends"
],
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"shared": true,
"ownerId": "admin",
"folderId": null,
"username": "admin",
"favorite": false,
"iconId": null,
"naturalId": "admin:sales-assistant",
"permissions": {
"chat": true,
"edit": true,
"share": true,
"delete": true,
"write": true,
"changeOwner": true,
"copy": true,
"rename": true,
"assignTags": true
},
"ownerName": "acme Administrator",
"tags": []
},
{
"id": "00000000-0000-4000-8000-000000000002",
"name": "Scratch Assistant",
"description": "Temporary assistant for the delete example",
"suggestions": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"shared": false,
"ownerId": "admin",
"folderId": null,
"username": "admin",
"favorite": false,
"iconId": null,
"naturalId": "admin:scratch-assistant",
"permissions": {
"chat": true,
"edit": true,
"share": true,
"delete": true,
"write": true,
"changeOwner": true,
"copy": true,
"rename": true,
"assignTags": true
},
"ownerName": "acme Administrator",
"tags": []
}
]
Captured from the API examples test suite; ids and timestamps are normalized.

GET /api/assistants/{id}

Retrieve a single Assistant with full detail.

Authentication: Required (AI access via ai.verify)

Pre-blocks: assistant.lookup(params.id) (resolves via read_access)

Path Parameters:

ParameterTypeDescription
idstringAssistant id (raw UUID or compound ownerId:slug)

Response:

A HAL representation of the Assistant with the inf:assistant-* rels. When a chat owned by this Assistant exists for the current user, the handler injects a chatId onto the response (absent in this fixture, since no such chat exists).

Get an assistantGET /api/assistants/admin:sales-assistant
GET /api/assistants/{id} resolves via read_access (ai.verify). `{id}` accepts the raw uuid OR the compound natural id `ownerId:slug`. When a chat owned by this assistant exists for the current user, `chatId` is injected onto the response (absent here). Response is HAL with the assistant-* rels.
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant"
},
"inf:draft": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/draft"
},
"inf:edit": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/_edit"
},
"inf:comments": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/comments{?sort,limit}",
"templated": true
},
"inf:assistant-share": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/shares/{principalId}",
"templated": true
},
"inf:assistant-owner": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/owner"
},
"inf:assistant-datasets": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/datasets"
},
"inf:assistant-skills": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/skills"
},
"inf:assistant-toolkits": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/toolkits"
},
"inf:assistant-references": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/references"
},
"inf:assistant-shares": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/shares"
},
"inf:assistant-icon": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/icon"
},
"inf:assistant-secrets": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/secrets"
},
"inf:assistant-copy": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/_copy"
},
"inf:favorite": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/favorite"
},
"inf:assistant-files": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/files"
},
"inf:assistant-file-upload": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/files/_upload"
},
"inf:assistant-usage": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/usage"
},
"inf:create-image": {
"href": "https://informer.example.com/api/assistants/admin%3Asales-assistant/_create-image"
},
"inf:chat-external": {
"href": "https://informer.example.com/assistants/admin%3Asales-assistant/chat"
}
},
"naturalId": "admin:sales-assistant",
"searchName": "sales assistant",
"permissions": {
"chat": true,
"edit": true,
"share": true,
"delete": true,
"write": true,
"changeOwner": true,
"copy": true,
"rename": true,
"assignTags": true
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "Sales Assistant",
"description": "Helps with sales data analysis",
"slug": "sales-assistant",
"ownerId": "admin",
"welcome": "Hello! How can I help with sales today?",
"modelId": "00000000-0000-4000-8000-000000000002",
"instructions": "You are a helpful sales analyst.",
"suggestions": [
"Show me this month's top deals",
"Analyze quarterly trends"
],
"token": null,
"data": null,
"source": null,
"sourceId": null,
"folderId": null,
"editingId": null,
"shared": true,
"embedded": false,
"dispatcher": false,
"libraryId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"datasets": [],
"editing": null,
"model": {
"naturalId": "acme-custom-opus",
"permissions": {},
"promptPpm": null,
"completionPpm": null,
"cacheReadPpm": null,
"cacheWriteShortPpm": null,
"cacheWriteLongPpm": null,
"id": "00000000-0000-4000-8000-000000000002",
"model": "claude-opus-4",
"slug": "acme-custom-opus",
"provider": {
"permissions": {},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000003",
"name": "Anthropic",
"slug": "anthropic-1",
"type": "anthropic",
"key": null,
"managed": true,
"internal": false,
"settings": null,
"data": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
},
"icon": null
}
Captured from the API examples test suite; ids and timestamps are normalized.

POST /api/assistants

Create an Assistant.

Authentication: Required (AI access via ai.verify)

Permission: assistants:create

Pre-blocks: ai.verify, then permission.assistants.create (requires data wizard + AI)

Request Body:

The payload is stripUnknown, so unrecognized fields are dropped.

FieldTypeRequiredDescription
namestringYesDisplay name
descriptionstringNoDescription
instructionsstringNoSystem instructions
welcomestringNoWelcome message
dispatcherbooleanNoWhether this is a dispatcher Assistant (default false)
suggestionsarrayNoSuggested prompts
modelstringNoPreferred model id
tokenstringNoAPI token for external access (stored encrypted)
dataobjectNoCustom metadata
iconstringNoIcon identifier
sharedbooleanNoWhether the Assistant is team-shared (default false)
The create key is model, not modelId

On create, the preferred-model key is model (a model id string). The stored column is modelId, and a beforeCreate hook defaults it to the go_everyday managed model when model is omitted. The update route uses modelId instead (see PUT below).

ownerId is server-derived from the caller, and token is encrypted at rest and never returned raw.

Example Request:

{
"name": "Marketing Assistant",
"description": "Helps with marketing campaign analysis",
"instructions": "You are an expert marketing analyst.",
"welcome": "Hi! I can help analyze marketing campaigns.",
"suggestions": [
"Show campaign performance",
"Analyze email open rates"
],
"icon": "megaphone",
"shared": true
}

Response:

Responds 201 Created with the created Assistant and a Location header. Runs in a database transaction.

Create an assistantPOST /api/assistants
Requires AI access (ai.verify) and the assistants:create permission (data wizard + AI). Payload is stripUnknown. The preferred-model key in the CREATE body is `model` (a model id string), NOT `modelId`; the stored column is `modelId` and a beforeCreate hook defaults it to the go_everyday managed model when omitted. `token` is encrypted at rest and never returned raw; `ownerId` is server-derived from the caller. Responds 201 with a Location header.
Request body
{
"name": "Marketing Assistant",
"description": "Helps with marketing campaign analysis",
"instructions": "You are an expert marketing analyst.",
"welcome": "Hi! I can help analyze marketing campaigns.",
"suggestions": [
"Show campaign performance",
"Analyze email open rates"
],
"icon": "megaphone",
"shared": true
}
Response · 201
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant"
},
"inf:draft": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/draft"
},
"inf:edit": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/_edit"
},
"inf:comments": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/comments{?sort,limit}",
"templated": true
},
"inf:assistant-share": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/shares/{principalId}",
"templated": true
},
"inf:assistant-owner": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/owner"
},
"inf:assistant-datasets": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/datasets"
},
"inf:assistant-skills": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/skills"
},
"inf:assistant-toolkits": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/toolkits"
},
"inf:assistant-references": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/references"
},
"inf:assistant-shares": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/shares"
},
"inf:assistant-icon": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/icon"
},
"inf:assistant-secrets": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/secrets"
},
"inf:assistant-copy": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/_copy"
},
"inf:favorite": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/favorite"
},
"inf:assistant-files": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/files"
},
"inf:assistant-file-upload": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/files/_upload"
},
"inf:assistant-usage": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/usage"
},
"inf:create-image": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/_create-image"
},
"inf:chat-external": {
"href": "https://informer.example.com/assistants/admin%3Amarketing-assistant/chat"
}
},
"naturalId": "admin:marketing-assistant",
"searchName": "marketing assistant",
"permissions": {
"chat": true,
"edit": true,
"share": true,
"delete": true,
"write": true,
"changeOwner": true,
"copy": true,
"rename": true,
"assignTags": true
},
"id": "00000000-0000-4000-8000-000000000001",
"embedded": false,
"dispatcher": false,
"tenant": "acme",
"name": "Marketing Assistant",
"description": "Helps with marketing campaign analysis",
"instructions": "You are an expert marketing analyst.",
"welcome": "Hi! I can help analyze marketing campaigns.",
"suggestions": [
"Show campaign performance",
"Analyze email open rates"
],
"shared": true,
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z",
"ownerId": "admin",
"modelId": "00000000-0000-4000-8000-000000000002",
"slug": "marketing-assistant",
"secrets": null,
"data": null,
"editingId": null,
"folderId": null,
"source": null,
"sourceId": null,
"token": null,
"libraryId": null
}
Captured from the API examples test suite; ids and timestamps are normalized.

PUT /api/assistants/{id}

Update an Assistant.

Authentication: Required (AI access via ai.verify)

Permission: assistant:write

Pre-blocks: ai.verify, assistant.lookup(params.id), model lookup (only when modelId is provided), then permission.assistant.write(pre.assistant)

Path Parameters:

ParameterTypeDescription
idstringAssistant id (raw UUID or compound ownerId:slug)

Request Body:

Unlike create, the model key here is modelId. The model lookup pre-block is skipped when modelId is absent, preserving the existing model. Many fields accept null or "" to clear them.

FieldTypeDescription
namestringUpdate display name
descriptionstring | nullUpdate description (null clears)
instructionsstring | nullUpdate system instructions (null clears)
modelIdstring | nullChange preferred model (null clears)
tokenstring | nullUpdate API token (null clears)
dispatcherbooleanToggle dispatcher mode
suggestionsarray | nullUpdate suggested prompts (null clears)
welcomestring | nullUpdate welcome message (null clears)
dataobject | nullUpdate custom metadata (null clears)
iconstring | nullUpdate icon (null clears)
sharedbooleanToggle team sharing

Example Request:

{
"name": "Updated Marketing Assistant",
"instructions": "Enhanced marketing analysis instructions.",
"suggestions": [
"Campaign ROI breakdown",
"Channel attribution"
]
}

Response:

Responds 200 with the updated Assistant (resolved via the read_access scope), not 201. Runs in a database transaction.

Update an assistantPUT /api/assistants/admin:marketing-assistant
PUT /api/assistants/{id} requires ai.verify + read_access + the assistant:write permission. Unlike create, the model key here is `modelId` (the model lookup pre is skipped when absent, preserving the existing model). Many fields accept null/empty-string to clear them. Runs in a transaction; returns the updated assistant (200, not 201).
Request body
{
"name": "Updated Marketing Assistant",
"instructions": "Enhanced marketing analysis instructions.",
"suggestions": [
"Campaign ROI breakdown",
"Channel attribution"
]
}
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant"
},
"inf:draft": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/draft"
},
"inf:edit": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/_edit"
},
"inf:comments": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/comments{?sort,limit}",
"templated": true
},
"inf:assistant-share": {
"href": "https://informer.example.com/api/assistants/admin%3Amarketing-assistant/shares/{principalId}",
"templated": true
},
"inf:assistant-owner": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/owner"
},
"inf:assistant-datasets": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/datasets"
},
"inf:assistant-skills": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/skills"
},
"inf:assistant-toolkits": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/toolkits"
},
"inf:assistant-references": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/references"
},
"inf:assistant-shares": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/shares"
},
"inf:assistant-icon": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/icon"
},
"inf:assistant-secrets": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/secrets"
},
"inf:assistant-copy": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/_copy"
},
"inf:favorite": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/favorite"
},
"inf:assistant-files": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/files"
},
"inf:assistant-file-upload": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/files/_upload"
},
"inf:assistant-usage": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/usage"
},
"inf:create-image": {
"href": "https://informer.example.com/api/assistants/admin%3Aupdated-marketing-assistant/_create-image"
},
"inf:chat-external": {
"href": "https://informer.example.com/assistants/admin%3Aupdated-marketing-assistant/chat"
}
},
"naturalId": "admin:updated-marketing-assistant",
"searchName": "updated marketing assistant",
"permissions": {
"chat": true,
"edit": true,
"share": true,
"delete": true,
"write": true,
"changeOwner": true,
"copy": true,
"rename": true,
"assignTags": true
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "Updated Marketing Assistant",
"description": "Helps with marketing campaign analysis",
"slug": "updated-marketing-assistant",
"ownerId": "admin",
"welcome": "Hi! I can help analyze marketing campaigns.",
"modelId": "00000000-0000-4000-8000-000000000002",
"instructions": "Enhanced marketing analysis instructions.",
"suggestions": [
"Campaign ROI breakdown",
"Channel attribution"
],
"token": null,
"data": null,
"source": null,
"sourceId": null,
"folderId": null,
"editingId": null,
"shared": true,
"embedded": false,
"dispatcher": false,
"libraryId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"model": {
"naturalId": "go_everyday",
"permissions": {},
"promptPpm": null,
"completionPpm": null,
"cacheReadPpm": null,
"cacheWriteShortPpm": null,
"cacheWriteLongPpm": null,
"id": "00000000-0000-4000-8000-000000000002",
"model": null,
"slug": "go_everyday",
"provider": {
"permissions": {},
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000003",
"name": "Anthropic",
"slug": "anthropic",
"type": "anthropic",
"key": null,
"managed": true,
"internal": false,
"settings": null,
"data": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
},
"icon": null
}
Captured from the API examples test suite; ids and timestamps are normalized.

DELETE /api/assistants/{id}

Delete an Assistant permanently.

Authentication: Required (AI access via ai.verify)

Permission: assistant:delete

Pre-blocks: ai.verify, assistant.lookup(params.id), then permission.assistant.delete(pre.assistant)

Path Parameters:

ParameterTypeDescription
idstringAssistant id (raw UUID or compound ownerId:slug)

Response:

Responds 200 with an empty body (Informer convention), not 204. Runs in a database transaction.

Delete an assistantDELETE /api/assistants/admin:scratch-assistant
DELETE /api/assistants/{id} requires ai.verify + read_access + the assistant:delete permission. It returns 200 with an EMPTY body (Informer convention), NOT 204. Cascades to the assistant's skills, datasets, secrets, files, and references. This cannot be undone.
Response · 200 · no body
Captured from the API examples test suite; ids and timestamps are normalized.
Permanent deletion

Deleting an Assistant cascades to its skills, datasets, secrets, files, and references. This cannot be undone.


GET /api/assistant-skill-templates

List the skill templates available when adding skills to an Assistant.

Authentication: Required (AI access via ai.verify)

Response:

A HAL collection (rel inf:skill-template) built from the registered assistant-skill-builder driver catalog via the DriverManager discover. Use a template's id when adding a skill to an Assistant.

List skill templatesGET /api/assistant-skill-templates
GET /api/assistant-skill-templates returns the registered assistant-skill-builder driver catalog as a HAL collection (rel inf:skill-template). Use these template ids when adding skills to an assistant. Requires AI access (ai.verify).
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistant-skill-templates"
}
},
"items": [
{
"detective": "00000000-0000-4000-8000-000000000001",
"id": "00000000-0000-4000-8000-000000000001:0",
"value": {
"group": "__top",
"name": "Simple Function",
"image": "/images/icons/simple-function.svg",
"skill": {
"type": "simpleFunction"
}
}
},
{
"detective": "00000000-0000-4000-8000-000000000002",
"id": "00000000-0000-4000-8000-000000000002:0",
"value": {
"group": "__top",
"name": "API Request",
"image": "/images/icons/api-request.svg",
"skill": {
"type": "apiRequest"
}
}
},
{
"detective": "00000000-0000-4000-8000-000000000003",
"id": "00000000-0000-4000-8000-000000000003:0",
"value": {
"group": "__top",
"name": "Highcharts",
"image": "/images/icons/highcharts.svg",
"skill": {
"type": "highcharts",
"name": "Highcharts"
}
}
},
"… 3 more items (6 total) — omitted from docs"
],
"start": 0,
"count": 6,
"total": 6
}
Captured from the API examples test suite; ids and timestamps are normalized.

GET /api/assistants/{id}/usage

Get usage statistics for an Assistant.

Authentication: Required (AI access via ai.verify)

Pre-blocks: ai.verify, then assistant.lookup(params.id)

Path Parameters:

ParameterTypeDescription
idstringAssistant id (raw UUID or compound ownerId:slug)

Query Parameters:

ParameterTypeDefaultDescription
monthstringcurrent monthMonth to aggregate the summary over
monthsinteger12Trailing months for the history rollup (1-24)

Response:

Returns { summary, history }. summary is the monthly usage aggregate from audit.ai_log for the requested month; history is the trailing-months monthly credits rollup. Both are pure SQL aggregates with no model-provider call. With no usage logged, summary reports zeroes and history is an empty array.

Get assistant usage statisticsGET /api/assistants/00000000-0000-4000-8000-000000000001/usage
GET /api/assistants/{id}/usage returns { summary, history }. `summary` is the monthly usage aggregate from audit.ai_log for the `month` query param (defaults to the current month); `history` is the trailing `months` (1-24, default 12) monthly credits rollup. Both are pure SQL aggregates (no model provider call). With no usage logged, summary is empty and history is an empty array. Requires AI access (ai.verify).
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/00000000-0000-4000-8000-000000000001/usage"
}
},
"summary": {
"count": 0,
"total": 0,
"average": 0
},
"history": []
}
Captured from the API examples test suite; ids and timestamps are normalized.

POST /api/assistants/{id}/_copy

Duplicate an Assistant (its instructions, skills, and other configuration) into a new Assistant.

Authentication: Required (AI access + permission.assistant.copy + permission.assistants.create)

Request Body:

FieldTypeRequiredDescription
namestringNoName for the copy. Defaults to a name derived from the original.

Returns 201 with the new Assistant and a Location header pointing at it.

Copy an assistantPOST /api/assistants/admin:order-desk-assistant/_copy
POST /api/assistants/{id}/_copy duplicates the assistant (instructions, skills, etc.). Payload { name? } sets the copy’s name (stripUnknown). Requires assistant:copy and assistants:create. Returns 201 with the new assistant and a Location header.
Request body
{
"name": "Order Desk Assistant (copy)"
}
Response · 201
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy"
},
"inf:draft": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/draft"
},
"inf:edit": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/_edit"
},
"inf:comments": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/comments{?sort,limit}",
"templated": true
},
"inf:assistant-share": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/shares/{principalId}",
"templated": true
},
"inf:assistant-owner": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/owner"
},
"inf:assistant-datasets": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/datasets"
},
"inf:assistant-skills": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/skills"
},
"inf:assistant-toolkits": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/toolkits"
},
"inf:assistant-references": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/references"
},
"inf:assistant-shares": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/shares"
},
"inf:assistant-icon": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/icon"
},
"inf:assistant-secrets": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/secrets"
},
"inf:assistant-copy": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/_copy"
},
"inf:favorite": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/favorite"
},
"inf:assistant-files": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/files"
},
"inf:assistant-file-upload": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/files/_upload"
},
"inf:assistant-usage": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/usage"
},
"inf:create-image": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant-copy/_create-image"
},
"inf:chat-external": {
"href": "https://informer.example.com/assistants/admin%3Aorder-desk-assistant-copy/chat"
}
},
"naturalId": "admin:order-desk-assistant-copy",
"searchName": "order desk assistant (copy)",
"permissions": {
"chat": true,
"edit": true,
"share": true,
"delete": true,
"write": true,
"changeOwner": true,
"copy": true,
"rename": true,
"assignTags": true
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "Order Desk Assistant (copy)",
"description": "Answers questions about orders.",
"slug": "order-desk-assistant-copy",
"ownerId": "admin",
"welcome": null,
"modelId": "00000000-0000-4000-8000-000000000002",
"instructions": null,
"suggestions": null,
"token": null,
"data": null,
"source": null,
"sourceId": null,
"folderId": null,
"shared": false,
"embedded": false,
"dispatcher": false,
"libraryId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"secrets": null,
"editingId": null
}
Captured from the API examples test suite; ids and timestamps are normalized.

POST /api/assistants/{id}/_takeover

Transfer ownership of an in-progress draft Assistant to the caller. Use this when another user left a draft open and you need to continue editing it.

Authentication: Required (AI access + permission.assistant.write)

The \{id\} is the draft's id (the row whose editingId points at the original, as returned by POST /api/assistants/\{id\}/_edit). Responds 400 if the id is not a draft. Returns 200 with an empty body; it is a no-op when you already own the draft.

Take over a draft assistantPOST /api/assistants/00000000-0000-4000-8000-000000000001/_takeover
POST /api/assistants/{id}/_takeover transfers ownership of an in-progress draft assistant to the caller (used when another user left a draft open). The {id} is the DRAFT’s id (the row whose editingId points at the original). 400 if the id is not a draft; requires assistant:write. Returns 200 with an empty body; it is a no-op when you already own the draft.
Response · 200 · no body
Captured from the API examples test suite; ids and timestamps are normalized.

Endpoints not captured here

The Assistant module also exposes live-AI and binary routes that depend on a model provider or on file IO against live services, so they are documented by contract only and have no captured response:

  • POST /api/assistants/\{id\}/_create-image generates an Assistant image with a model provider.
  • GET /api/assistants/\{id\}/icon and GET /api/assistants/\{id\}/favicon.ico return image bytes.
  • The Assistant file upload and download routes stream binary content.
  • The Assistant chat route streams a live model response.

Skills, datasets, secrets, toolkits, references, sharing, ownership, drafts, comments, and favorites each live on their own page in this section.