Skip to main content

Chat Associations

Endpoints for managing which Toolkits are enabled in a specific Chat. Listing a Chat's Toolkits resolves the Chat by id and is not AI-gated. Enabling or disabling a Toolkit is AI-gated (the Toolkit is resolved through toolkit.lookup, which runs ai.verify first) and additionally requires that the caller own the Chat.

GET /api/chats/{chatId}/toolkits

List the Toolkits enabled for a Chat.

Authentication: Required

Pre-blocks: chat lookup (Chat.findById -> 404 "Chat not found" when missing)

This route is not AI-gated and performs no ownership check; any authenticated caller who can see the Chat may list its Toolkits.

Path Parameters:

ParameterTypeDescription
chatIdstringChat id

Response:

A HAL collection under _embedded["inf:toolkit"]. Each embedded item is the full Toolkit row enabled for the Chat (not a chat-toolkit join row): the handler maps each enabled association to its Toolkit and embeds those. There is no start/count/total envelope.

List a chat’s toolkitsGET /api/chats/00000000-0000-4000-8000-000000000001/toolkits
GET /api/chats/{chatId}/toolkits returns a HAL collection embedding `inf:toolkit`. The chat is resolved by id (404 "Chat not found" when missing); the route is NOT AI-gated and has NO ownership check. Each embedded item is the full Toolkit row enabled for the chat (not the chat-toolkit join row), keyed under `_embedded["inf:toolkit"]` over the handler’s `items` array.
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/chats/00000000-0000-4000-8000-000000000001/toolkits"
}
},
"_embedded": {
"inf:toolkit": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/toolkits/00000000-0000-4000-8000-000000000002"
}
},
"naturalId": "admin:github-integration",
"allowUserInstances": true,
"executionLocation": "server",
"canManageTools": true,
"permissions": {
"write": true,
"edit": true,
"delete": true,
"share": true,
"assignTags": true,
"changeOwner": true
},
"id": "00000000-0000-4000-8000-000000000002",
"name": "GitHub Integration",
"slug": "github-integration",
"type": "custom",
"description": "Access GitHub repositories",
"instructions": null,
"ownerId": "admin",
"shared": false,
"folderId": null,
"config": null,
"integrationId": null,
"source": null,
"sourceId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"integration": null
}
]
}
}
Captured from the API examples test suite; ids and timestamps are normalized.

PUT /api/chats/{chatId}/toolkits/{id}

Enable a Toolkit for a Chat. The association is upserted, so re-enabling an already-enabled Toolkit is idempotent.

Authentication: Required

Pre-blocks:

  1. chat lookup (chat ownership enforced: chat.username must equal the caller's username, otherwise 403; a missing Chat answers 404)
  2. toolkit.lookup (runs ai.verify first, so AI must be enabled on the tenant; an unknown Toolkit id answers 404)

The {id} path parameter is the Toolkit's id (a UUID resolved through toolkit.lookup), not a naturalId.

Path Parameters:

ParameterTypeDescription
chatIdstringChat id
idstringToolkit id (UUID)

Request Body:

A JSON body is required. The Informer GO client sends { "enabled": true }. The handler ignores the body's contents, but a body-less PUT is rejected by payload validation.

{ "enabled": true }

Response:

Responds 200 with the persisted ChatToolkit join row ({ tenant, chatId, toolkitId, createdAt, updatedAt }). The handler does not use .created(), so the status is 200, not 201.

Enable a toolkit in a chatPUT /api/chats/00000000-0000-4000-8000-000000000001/toolkits/00000000-0000-4000-8000-000000000002
PUT /api/chats/{chatId}/toolkits/{id} upserts the chat<->toolkit join and returns the persisted ChatToolkit row { tenant, chatId, toolkitId, createdAt, updatedAt }. The handler does NOT use .created(), so the status is 200 (not 201). `{id}` is the toolkit id, resolved via toolkit.lookup which runs ai.verify() (AI must be enabled). Requires chat ownership: a chat owned by another user -> 403; a missing chat -> 404; an unknown toolkit id -> 404. Send a JSON body (the GO client sends { enabled: true }); the handler ignores its contents, but a body-less PUT is rejected by payload validation.
Request body
{
"enabled": true
}
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/chats/00000000-0000-4000-8000-000000000001/toolkits/00000000-0000-4000-8000-000000000002"
}
},
"id": "00000000-0000-4000-8000-000000000003",
"tenant": "acme",
"chatId": "00000000-0000-4000-8000-000000000001",
"toolkitId": "00000000-0000-4000-8000-000000000002",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
Captured from the API examples test suite; ids and timestamps are normalized.
Enabling makes tools available

Once a Toolkit is enabled, its tools become available to the AI when it responds in this Chat.


DELETE /api/chats/{chatId}/toolkits/{id}

Disable a Toolkit for a Chat by destroying the association row.

Authentication: Required

Pre-blocks:

  1. chat lookup (chat ownership enforced: chat.username must equal the caller's username, otherwise 403; a missing Chat answers 404)
  2. toolkit.lookup (runs ai.verify first, so AI must be enabled on the tenant; an unknown Toolkit id answers 404)

Path Parameters:

ParameterTypeDescription
chatIdstringChat id
idstringToolkit id (UUID)

Response:

Responds 204 No Content with an empty body. This is the literal route behavior: the handler explicitly calls h.response().code(204), unlike most Informer DELETE routes which return 200. Disabling a Toolkit that is not currently enabled still answers 204 (the destroy is a no-op).

Disable a toolkit in a chatDELETE /api/chats/00000000-0000-4000-8000-000000000001/toolkits/00000000-0000-4000-8000-000000000002
DELETE /api/chats/{chatId}/toolkits/{id} destroys the join row and explicitly responds 204 with an empty body (unlike most Informer DELETEs, which return 200). Shares the PUT pres: chat ownership (403 otherwise) and toolkit.lookup (AI-gated, 404 on unknown id). Disabling a toolkit that is not enabled still returns 204 (the destroy is a no-op).
Response · 204 · no body
Captured from the API examples test suite; ids and timestamps are normalized.
Disabling preserves history

Disabling a Toolkit removes its tools from the Chat going forward. Existing Chat history is unchanged.


Use Cases

Project-specific tools

Enable different Toolkits for different Chats. The {id} is the Toolkit's UUID:

# Sales Chat: enable a CRM Toolkit
PUT /api/chats/{salesChatId}/toolkits/{crmToolkitId}

# Dev Chat: enable a GitHub Toolkit
PUT /api/chats/{devChatId}/toolkits/{githubToolkitId}

Dynamic tool access

Enable a Toolkit for the duration of a task, then disable it when done:

// Enable filesystem access for a code-review Chat
await fetch(`/api/chats/${chatId}/toolkits/${filesystemToolkitId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ enabled: true })
});

// Disable it after the review is complete
await fetch(`/api/chats/${chatId}/toolkits/${filesystemToolkitId}`, {
method: 'DELETE'
});

Security boundaries

Limit sensitive tool access to specific Chats:

# Only enable a database-admin Toolkit for an admin Chat
PUT /api/chats/{adminChatId}/toolkits/{databaseAdminToolkitId}

Best Practices

  • Minimal access: only enable the Toolkits a Chat actually needs.
  • Context-aware: match Toolkits to the Chat's topic.
  • Cleanup: disable Toolkits when they are no longer needed.
  • Audit: review which Toolkits are enabled for sensitive Chats.