AI Instructions
AI instructions are reusable prompt-text presets, each addressed by a
tenant-unique slug. Assistants and other AI surfaces splice an instruction's
content in via a ${slug} reference, so a single preset can carry house voice,
citation rules, or tone guidance into many AI conversations without copying the
text.
Every instruction route is gated by the AI module. The route lifecycle runs
ai.verify first, which asserts both that the tenant's AI master switch is on
and that the caller carries the ai credential. When AI is off the route
answers 400 ("AI is not enabled on the system"); when AI is on but the
caller lacks AI access it answers 403.
All five routes below require AI to be enabled on the tenant. The read routes
(GET list and single) add no further permission check, so any AI-enabled
caller may read instructions within the tenant. The write routes layer
per-action permissions on top, described in each section.
An instruction has these properties:
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Instruction id |
slug | string | Tenant-unique URL-safe name used in ${slug} references |
description | string | null | Optional human-readable description |
content | string | The prompt text spliced into AI surfaces |
ownerId | string | Username of the owner; defaults to the creator |
createdAt / updatedAt | date | Timestamps |
The {id} path segment accepts either a UUID or a slug:
AiInstruction.findById resolves by UUID when the value looks like one and
falls back to slug otherwise.
GET /api/ai-instructions
List the tenant's AI instructions.
Authentication: AI enabled (ai.verify); no permission check
Response:
A HAL collection (rel inf:ai-instructions) with rows embedded under
_embedded["inf:ai-instruction"]. A postQuery hook attaches a permissions
object (req.permissions('aiInstruction')) to the collection so the client can
render per-action affordances.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/ai-instructions"
}
},
"start": 0,
"count": 3,
"total": 3,
"permissions": {
"edit": false,
"delete": false,
"write": false,
"changeOwner": true,
"copy": true
},
"_embedded": {
"inf:ai-instruction": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/ai-instructions/00000000-0000-4000-8000-000000000001"
}
},
"permissions": {},
"id": "00000000-0000-4000-8000-000000000001",
"slug": "order-desk-tone",
"description": "House voice for Order Desk replies",
"ownerId": "admin",
"content": "You are the Order Desk assistant. Answer concisely, cite the order number, and never speculate about delivery dates you cannot confirm.",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/ai-instructions/00000000-0000-4000-8000-000000000002"
}
},
"permissions": {},
"id": "00000000-0000-4000-8000-000000000002",
"slug": "cite-sources",
"description": "Require source citations in answers",
"ownerId": "admin",
"content": "When you state a fact drawn from a dataset, cite the dataset and field it came from.",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/ai-instructions/00000000-0000-4000-8000-000000000003"
}
},
"permissions": {},
"id": "00000000-0000-4000-8000-000000000003",
"slug": "scratch-instruction",
"description": null,
"ownerId": "admin",
"content": "Temporary instruction used only to demonstrate deletion.",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
}
]
}
}
POST /api/ai-instructions
Create an AI instruction.
Authentication: AI enabled (ai.verify) + permission.aiInstructions.create
The create permission is satisfied by any caller carrying the ai credential.
It is not Superuser-gated.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | Tenant-unique slug for ${slug} references |
description | string | No | Description. A blank string or null is dropped |
content | string | Yes | The prompt text |
The payload is stripUnknown, so unrecognized fields are dropped. The new row's
ownerId defaults to the caller's username.
Example Request:
{
"slug": "quarterly-report-voice",
"description": "Tone and structure for quarterly report narratives",
"content": "Write the quarterly summary in plain business English. Lead with the headline number, then explain the drivers in at most three sentences."
}
Response:
Responds 201 Created with the instruction and a Location header. The
Location points at the instruction by slug, not UUID.
{
"slug": "quarterly-report-voice",
"description": "Tone and structure for quarterly report narratives",
"content": "Write the quarterly summary in plain business English. Lead with the headline number, then explain the drivers in at most three sentences."
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/ai-instructions/quarterly-report-voice"
}
},
"permissions": {},
"id": "00000000-0000-4000-8000-000000000001",
"tenant": "acme",
"slug": "quarterly-report-voice",
"description": "Tone and structure for quarterly report narratives",
"content": "Write the quarterly summary in plain business English. Lead with the headline number, then explain the drivers in at most three sentences.",
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z",
"ownerId": "admin"
}
A slug is unique within the tenant. A missing slug or content answers
400 at validation.
GET /api/ai-instructions/{id}
Retrieve a single AI instruction.
Authentication: AI enabled (ai.verify); no permission check
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Instruction UUID or slug |
Response:
The single instruction (HAL rel inf:ai-instruction). There is no per-row
permission check on read; the only gate is ai.verify.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/ai-instructions/00000000-0000-4000-8000-000000000001"
}
},
"permissions": {},
"id": "00000000-0000-4000-8000-000000000001",
"slug": "order-desk-tone",
"description": "House voice for Order Desk replies",
"ownerId": "admin",
"content": "You are the Order Desk assistant. Answer concisely, cite the order number, and never speculate about delivery dates you cannot confirm.",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
}
PUT /api/ai-instructions/{id}
Update an AI instruction.
Authentication: AI enabled (ai.verify) + permission.aiInstruction.edit
The edit permission is granted to the owner or a Superuser, both of which also need AI access.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Instruction UUID or slug |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
description | string | No | Description. A blank string or null is dropped |
content | string | Yes | The replacement prompt text |
The payload is stripUnknown. content is required on every update because it
is a full replacement of the content, not a patch.
slug is stripped from the payload, so a PUT that tries to rename the slug
silently keeps the original.
Example Request:
{
"description": "House voice for Order Desk replies (refined)",
"content": "You are the Order Desk assistant. Answer concisely, cite the order number, and escalate to a human when a refund is requested."
}
Response:
Responds 200 with the updated instruction.
{
"description": "House voice for Order Desk replies (refined)",
"content": "You are the Order Desk assistant. Answer concisely, cite the order number, and escalate to a human when a refund is requested."
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/ai-instructions/00000000-0000-4000-8000-000000000001"
}
},
"permissions": {},
"id": "00000000-0000-4000-8000-000000000001",
"slug": "order-desk-tone",
"description": "House voice for Order Desk replies (refined)",
"ownerId": "admin",
"content": "You are the Order Desk assistant. Answer concisely, cite the order number, and escalate to a human when a refund is requested.",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
}
DELETE /api/ai-instructions/{id}
Delete an AI instruction.
Authentication: AI enabled (ai.verify) + permission.aiInstruction.delete
The delete permission is granted to the owner or a Superuser. Unlike create and
edit, the delete permission driver does not itself require the ai credential,
but the route's ai.verify pre still does, so AI must be enabled.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Instruction UUID or slug |
Response:
Responds 200 with an empty body (Informer's db.remove convention), not
204.
Deleting an instruction is permanent. Any AI surface that referenced it via
${slug} will no longer resolve that reference.