Secrets
Endpoints for managing the Secrets an Assistant uses. A Secret is a named
credential (an API key, token, or other value) that an Assistant references when
running a Skill. Each Secret has a key (its name) and an encrypted value.
Every route on this page is gated by ai.verify (AI must be enabled on the
tenant AND the caller must have AI access). The host Assistant is resolved by
assistant.lookup(params.id) through the read_access scope, so a missing or
inaccessible Assistant answers 404. The three mutating routes (POST, PUT,
DELETE) additionally require permission.assistant.write on the Assistant.
A Secret's value is encrypted at rest and is never returned in any
response. toHal strips the field entirely, so responses carry the key but
omit value altogether. There is no masked placeholder; the field is simply
absent. Only the Assistant can use the plaintext value, during Skill execution.
GET /api/assistants/{id}/secrets
List an Assistant's Secrets.
Authentication: Required (AI access via ai.verify)
Pre-blocks: assistant.lookup(params.id)
Response:
A HAL collection under _embedded["inf:assistant-secret"]. Each entry serializes
its key but not its value.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant/secrets"
}
},
"start": 0,
"count": 2,
"total": 2,
"_embedded": {
"inf:assistant-secret": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant/secrets/00000000-0000-4000-8000-000000000001"
}
},
"id": "00000000-0000-4000-8000-000000000001",
"key": "HUBSPOT_API_KEY",
"assistantId": "00000000-0000-4000-8000-000000000002",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant/secrets/00000000-0000-4000-8000-000000000003"
}
},
"id": "00000000-0000-4000-8000-000000000003",
"key": "SCRATCH_TOKEN",
"assistantId": "00000000-0000-4000-8000-000000000002",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
}
]
}
}
Secret properties:
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Secret id |
key | string | Secret name (e.g. HUBSPOT_API_KEY) |
assistantId | string (UUID) | Id of the owning Assistant |
createdAt / updatedAt | date | Timestamps |
POST /api/assistants/{id}/secrets
Add a Secret to an Assistant.
Authentication: Required (AI access via ai.verify)
Pre-blocks: assistant.lookup(params.id), permission.assistant.write
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Secret name (e.g. SLACK_BOT_TOKEN) |
value | string | null | No | Secret value; encrypted at rest and never echoed back |
There is no name or description field. The credential name is key.
Example Request:
{
"key": "SLACK_BOT_TOKEN",
"value": "xoxb-example-token"
}
Response:
Responds 201 Created with the new Secret (value stripped) and a Location
header to it.
{
"key": "SLACK_BOT_TOKEN",
"value": "xoxb-example-token"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant/secrets/00000000-0000-4000-8000-000000000001"
}
},
"id": "00000000-0000-4000-8000-000000000001",
"tenant": "acme",
"assistantId": "00000000-0000-4000-8000-000000000002",
"key": "SLACK_BOT_TOKEN",
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z"
}
GET /api/assistants/{id}/secrets/{secretId}
Retrieve a single Secret.
Authentication: Required (AI access via ai.verify)
Pre-blocks: assistant.lookup(params.id), Secret lookup (db.lookup)
Response:
The Secret with its value stripped. A missing or inaccessible secretId
answers 404.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant/secrets/00000000-0000-4000-8000-000000000001"
}
},
"id": "00000000-0000-4000-8000-000000000001",
"key": "SLACK_BOT_TOKEN",
"assistantId": "00000000-0000-4000-8000-000000000002",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
}
PUT /api/assistants/{id}/secrets/{secretId}
Update a Secret.
Authentication: Required (AI access via ai.verify)
Pre-blocks: assistant.lookup(params.id), Secret lookup, permission.assistant.write
Request Body:
| Field | Type | Description |
|---|---|---|
key | string | Rename the Secret |
value | string | null | Rotate the value; null or "" clears it. Re-encrypted on update |
Example Request:
{
"key": "SLACK_BOT_TOKEN",
"value": "xoxb-rotated-token"
}
Response:
Responds 200 with the updated Secret (value stripped). Runs in a database
transaction.
{
"key": "SLACK_BOT_TOKEN",
"value": "xoxb-rotated-token"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/assistants/admin%3Aorder-desk-assistant/secrets/00000000-0000-4000-8000-000000000001"
}
},
"id": "00000000-0000-4000-8000-000000000001",
"key": "SLACK_BOT_TOKEN",
"assistantId": "00000000-0000-4000-8000-000000000002",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
}
DELETE /api/assistants/{id}/secrets/{secretId}
Delete a Secret.
Authentication: Required (AI access via ai.verify)
Pre-blocks: assistant.lookup(params.id), Secret lookup, permission.assistant.write
Response:
Responds 200 with an empty body (Informer convention), not 204. Runs in a
database transaction.
Security Considerations
Encryption
- Secret values are encrypted at rest by the model's
beforeCreateandbeforeUpdatehooks. - The plaintext value is never serialized in any response; only the Assistant can read it during Skill execution.
Access Control
- All Secret routes require AI access (
ai.verify). - Creating, updating, and deleting Secrets requires
permission.assistant.writeon the owning Assistant. - Secrets are isolated per Assistant.
Best Practices
- Use descriptive keys (e.g.
GITHUB_TOKEN,STRIPE_SECRET_KEY). - Rotate values periodically with a PUT.
- Delete unused Secrets.
- Never hardcode Secrets in instructions or Skill configurations.