Agents & keys#
These endpoints manage the caller's own agents — their provider keys and their per-group dispatch settings. See Agents & forking for the concepts. All require authentication.
POST /api/getMyAgents#
List the agents the caller owns (agents whose parent_username is you), with whether a provider key is configured. Takes no body.
curl -X POST http://localhost:4000/api/getMyAgents \
-H "authorization: Bearer dev:ops"{
"ok": true,
"result": {
"items": [
{ "provider": "claude", "username": "ops:claude", "display_name": "Claude", "key_configured": true },
{ "provider": "deepseek", "username": "ops:deepseek", "display_name": "DeepSeek", "key_configured": false }
]
}
}POST /api/setAgentKey#
Register or replace your API key for a provider. The key is sealed (XChaCha20-Poly1305) server-side and only decrypted at invocation. The provider string must match the agent's agent_provider.
| Field | Type | Required | Description |
|---|---|---|---|
provider | string | yes | "claude" or "deepseek". |
api_key | string | yes | The provider API key. Must be non-empty. |
curl -X POST http://localhost:4000/api/setAgentKey \
-H "authorization: Bearer dev:ops" \
-H "content-type: application/json" \
-d '{ "provider": "claude", "api_key": "sk-ant-..." }'Returns the provider plus a masked tail for display (the raw key is never echoed):
{ "ok": true, "result": { "provider": "claude", "key_tail": "•••x7kQ" } }Provider naming
Use the agent provider (claude, deepseek) — not the vendor name. The credential is keyed by (owner, provider) and looked up via the @owner:provider handle, so claude is what powers @you:claude.
Errors — 400 if api_key is empty.
POST /api/removeAgentKey#
Clear your stored key for a provider.
| Field | Type | Required | Description |
|---|---|---|---|
provider | string | yes | Provider whose key to remove. |
curl -X POST http://localhost:4000/api/removeAgentKey \
-H "authorization: Bearer dev:ops" \
-H "content-type: application/json" \
-d '{ "provider": "claude" }'Returns { "ok": true }.
POST /api/getGroupAgents#
List the agents present in a group and their dispatch mode. The caller must be a participant.
| Field | Type | Required | Description |
|---|---|---|---|
chat_id | UUID | yes | Group conversation id. |
{
"ok": true,
"result": {
"items": [
{ "agent": { "username": "ops:claude", "kind": "agent", "display_name": "Claude" }, "always_on": false }
]
}
}POST /api/addGroupAgent#
Add one of your agents to a group, with an initial dispatch mode. The caller must be a participant and own the agent.
| Field | Type | Required | Description |
|---|---|---|---|
chat_id | UUID | yes | Group conversation id. |
agent_username | string | yes | Agent to add — must be owned by the caller. |
always_on | boolean | no | true to reply to every human message; false (default) is mention-only. |
curl -X POST http://localhost:4000/api/addGroupAgent \
-H "authorization: Bearer dev:ops" \
-H "content-type: application/json" \
-d '{ "chat_id": "0d6c…", "agent_username": "ops:claude", "always_on": false }'Returns { "ok": true }.
Errors — 403 if you're not a participant or not the agent's owner; 404 if the agent or conversation is missing; 400 if agent_username is not an agent.
POST /api/removeGroupAgent#
Remove one of your agents from a group (and clear its dispatch setting). Same params as below, minus always_on.
| Field | Type | Required | Description |
|---|---|---|---|
chat_id | UUID | yes | Group conversation id. |
agent_username | string | yes | Agent to remove — must be owned by the caller. |
Returns { "ok": true }.
POST /api/setGroupAgentMode#
Flip always_on for one of your agents in a group.
| Field | Type | Required | Description |
|---|---|---|---|
chat_id | UUID | yes | Group conversation id. |
agent_username | string | yes | Agent to reconfigure — must be owned by the caller. |
always_on | boolean | yes | New dispatch mode. |
curl -X POST http://localhost:4000/api/setGroupAgentMode \
-H "authorization: Bearer dev:ops" \
-H "content-type: application/json" \
-d '{ "chat_id": "0d6c…", "agent_username": "ops:claude", "always_on": true }'Returns { "ok": true }. The new mode takes effect on the next message — see the dispatch rules.
Per-user config overrides#
An agent's configuration (model / system prompt / API keys / …) is set by its owner via setBotConfig — one default shared by everyone. Any user who shares a conversation with the agent can additionally override their own values/secrets; at runtime the agent uses the triggering sender's override, falling back per-field to the owner default (untouched fields inherit the owner's value live).
POST /api/getMyBotConfig#
Read the effective form, the owner's current defaults, and your own override.
| Field | Type | Required | Description |
|---|---|---|---|
username | string | yes | The agent. Caller must share a conversation with it (or own it). |
Returns a MyBotConfig:
{
bot: Account;
template?: string | null;
config_schema: FieldSpec[]; // non-secret fields to render
secret_schema: FieldSpec[]; // secret fields (SecureField)
owner_config: Record<string,string>; // owner's current values (the starting point)
my_config: Record<string,string>; // YOUR sparse override (only changed keys)
my_secrets: { field: string; tail: string }[]; // masked last-4 of YOUR secrets
owner_secret_fields: string[]; // secret fields the owner has a default for
}POST /api/setMyBotConfig#
Save your own override. config is replace-sparse (the non-empty entries become your whole override; a field sent blank → dropped → inherits the owner default). secrets is merge-in (empty value clears that field → inherit; non-empty seals + stores it). Returns the updated MyBotConfig.
| Field | Type | Required | Description |
|---|---|---|---|
username | string | yes | The agent. |
config | object | no | { key: value } non-secret overrides. |
secrets | object | no | { field: plaintext } — sealed server-side, never echoed. |
curl -X POST http://localhost:4000/api/setMyBotConfig \
-H "authorization: Bearer dev:eons" \
-H "content-type: application/json" \
-d '{ "username": "ops:claude", "config": { "model": "my-model" }, "secrets": { "api_key": "sk-mine" } }'setBotConfig (owner-only) still sets the shared default; these endpoints never touch it.