Agents
Every call needs Authorization: Bearer <key>. 7 calls:
- List the models this workspace’s agents can use:
GET /v1/agent-models - List agent runs, newest first:
GET /v1/agent-runs - Get an agent run and its steps:
GET /v1/agent-runs/:id - List AI agents, newest first:
GET /v1/agents - Create an AI agent:
POST /v1/agents - Get an AI agent:
GET /v1/agents/:id - Edit an AI agent, or turn it on, put it in shadow or pause it:
PATCH /v1/agents/:id
Choose an agent’s model
Each agent answers with an AI model. bubbl3 turns models on and picks a default for AI agents; you choose one of those models for each agent under Model on the agent’s page, then Save changes. New agents start on the default, which the list marks.
- The list shows only the models bubbl3 has turned on for your workspace. A workspace with the HIPAA add-on only sees models covered by a business associate agreement.
- To answer, the agent sends the model exactly this, and nothing from other conversations or contacts:
- the agent’s instructions;
- the conversation’s recent messages (the last 30);
- the contact’s name, when a contact tool (Read the contact, Search contacts, Update the contact) or List conversations reads it;
- the contact’s phone number, only when Update the contact or Check iMessage is on. Otherwise it’s hidden in every common format (with or without +1, spaces, dashes, dots or brackets) everywhere in the request: messages, tool results and custom fields. Search contacts then doesn’t match on it;
- with the optional contact tools (Read the contact, Search contacts, Update the contact) on, the contact’s email, company, custom fields and labels;
- with List conversations on, the contact’s name, company and labels and a preview of the conversation’s last message.
- If bubbl3 turns an agent’s model off, the agent uses the default from its next run, and its page says so: “This agent’s model was turned off; it now uses …”. Saving the agent keeps its model, so it goes back to it once the model is on again.
- If there’s no model left for your workspace, the page says AI agents can’t answer until bubbl3 turns one on, and each conversation the agent would answer goes to a person instead, with the reason “No model available” on the run. A model turned off while a run is going ends that run the same way, with “Model turned off during the run”.
- Each run on the Runs tab shows the model it used.
From the API, call List the models this workspace’s agents can use and send one of its ids as the agent’s model when you create or edit it. Any other model is refused with a 400 on model.
List the models this workspace’s agents can use
GET /v1/agent-models
The AI models bubbl3 has turned on that this workspace may use, with the AI agents default marked. A workspace with the HIPAA add-on only sees models whose provider is covered by a business associate agreement. When the list is empty, agents hand every conversation to a person until bubbl3 turns a model on. An agent whose model is turned off later uses the default.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
Response 200
| Field | Type | Description |
|---|---|---|
data | object[] | |
data[].default | boolean | |
data[].id | string | |
data[].name | string | |
data[].provider_name | string | |
default_model | string or null | |
hipaa | boolean | |
unavailable | enum or null | One of: none_enabled, hipaa_no_baa, no_default. |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
List agent runs, newest first
GET /v1/agent-runs
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | From 1 to 100. Default 50. |
cursor | string | No | Opaque cursor from next_cursor. Up to 512 characters. |
agent_id | string (uuid) | No | |
conversation_id | string | No | Conversation id (cnv_...). |
outcome | enum | No | One of: running, resolved, handoff, error. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
Response 200
| Field | Type | Description |
|---|---|---|
data | object[] | |
data[].agent_id | string (uuid) | |
data[].conversation_id | string or null | Conversation id (cnv_...). |
data[].error_code | string or null | |
data[].finished_at | string (date-time) or null | ISO 8601 timestamp. |
data[].handoff_reason | string or null | |
data[].id | string (uuid) | |
data[].input_tokens | integer | From 0 to 9007199254740991. |
data[].model | string or null | |
data[].outcome | enum or null | One of: running, resolved, handoff, error. |
data[].output_tokens | integer | From 0 to 9007199254740991. |
data[].shadow | boolean | |
data[].started_at | string (date-time) | ISO 8601 timestamp. |
data[].tool_calls | integer | From 0 to 9007199254740991. |
data[].trigger_message_id | string or null | Message id (msg_...). |
data[].turns | integer | From 0 to 9007199254740991. |
data[].workflow_run_id | string (uuid) or null | |
next_cursor | string or null | Pass as cursor to get the next page; null at the end. |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Get an agent run and its steps
GET /v1/agent-runs/:id
What the agent said appears in the steps only for callers who can read conversations.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
Response 200
| Field | Type | Description |
|---|---|---|
agent_id | string (uuid) | |
conversation_id | string or null | Conversation id (cnv_...). |
error_code | string or null | |
finished_at | string (date-time) or null | ISO 8601 timestamp. |
handoff_reason | string or null | |
id | string (uuid) | |
input_tokens | integer | From 0 to 9007199254740991. |
model | string or null | |
outcome | enum or null | One of: running, resolved, handoff, error. |
output_tokens | integer | From 0 to 9007199254740991. |
shadow | boolean | |
started_at | string (date-time) | ISO 8601 timestamp. |
steps | object[] | |
steps[].at | string (date-time) | ISO 8601 timestamp. |
steps[].code | string | |
steps[].detail | object | |
steps[].kind | enum | One of: model, tool, message, handoff, guard. |
steps[].status | enum | One of: ok, refused, failed, shadowed. |
steps[].tool | string | |
tool_calls | integer | From 0 to 9007199254740991. |
trigger_message_id | string or null | Message id (msg_...). |
turns | integer | From 0 to 9007199254740991. |
workflow_run_id | string (uuid) or null |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
List AI agents, newest first
GET /v1/agents
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | From 1 to 100. Default 50. |
cursor | string | No | Opaque cursor from next_cursor. Up to 512 characters. |
status | enum | No | One of: draft, shadow, live, paused. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
Response 200
| Field | Type | Description |
|---|---|---|
data | object[] | |
data[].created_at | string (date-time) | ISO 8601 timestamp. |
data[].handoff_rules | object | |
data[].handoff_rules.keywords | string[] | Hand off, without asking the model, when the contact’s message has one of these. Up to 50 items. |
data[].handoff_rules.max_turns | integer | From 1 to 100. |
data[].handoff_rules.on_low_confidence | boolean | Hand off when the model answers with neither text nor a tool call. Default true. |
data[].handoff_rules.team_id | string (uuid) or null | Team the conversation is assigned to on handoff. |
data[].handoff_rules.user_id | string (uuid) or null | Person the conversation is assigned to on handoff. |
data[].id | string (uuid) | |
data[].instructions | string | |
data[].limits | object | |
data[].limits.max_messages_per_conversation | integer | From 1 to 100. |
data[].limits.max_runs_per_conversation_per_day | integer | From 1 to 200. |
data[].limits.max_tokens_per_run | integer | From 1 to 200000. |
data[].limits.max_tool_calls_per_run | integer | From 1 to 20. |
data[].limits.max_turns_per_run | integer | From 1 to 10. |
data[].model | string | |
data[].model_fallback | boolean | |
data[].model_in_use | object or null | |
data[].model_in_use.id | string | |
data[].model_in_use.name | string | |
data[].name | string | |
data[].pool_ids | string[] | |
data[].runs_7d | object | |
data[].runs_7d.conversations | integer | From 0 to 9007199254740991. |
data[].runs_7d.error | integer | From 0 to 9007199254740991. |
data[].runs_7d.handoff | integer | From 0 to 9007199254740991. |
data[].runs_7d.resolved | integer | From 0 to 9007199254740991. |
data[].runs_7d.total | integer | From 0 to 9007199254740991. |
data[].status | enum | One of: draft, shadow, live, paused. |
data[].tools | enum[] | |
data[].updated_at | string (date-time) | ISO 8601 timestamp. |
data[].usable_tools | enum[] | |
next_cursor | string or null | Pass as cursor to get the next page; null at the end. |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Create an AI agent
POST /v1/agents
Creates the agent and the service identity its tool calls run with. You can only give it tools your own role can use, and they stay capped by your role. Live and shadow agents need at least one pool. model must be one of GET /v1/agent-models; without it the agent starts on the AI agents default. Signed-in members only: API keys are refused.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
idempotency-key | string | No | Makes the write safe to retry for 24 hours. 1 to 255 printable characters. Keys starting with outreach:, workflow: or agent: are reserved for messages bubbl3 sends itself and are refused with 400. Up to 255 characters. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
handoff_rules | object | No | Default {"keywords":[],"on_low_confidence":true}. |
handoff_rules.keywords | string[] | No | Hand off, without asking the model, when the contact’s message has one of these. Up to 50 items. |
handoff_rules.max_turns | integer | No | From 1 to 100. |
handoff_rules.on_low_confidence | boolean | No | Hand off when the model answers with neither text nor a tool call. Default true. |
handoff_rules.team_id | string (uuid) or null | No | Team the conversation is assigned to on handoff. |
handoff_rules.user_id | string (uuid) or null | No | Person the conversation is assigned to on handoff. |
instructions | string | No | Up to 20000 characters. |
limits | object | No | Default {}. |
limits.max_messages_per_conversation | integer | No | From 1 to 100. |
limits.max_runs_per_conversation_per_day | integer | No | From 1 to 200. |
limits.max_tokens_per_run | integer | No | From 1 to 200000. |
limits.max_tool_calls_per_run | integer | No | From 1 to 20. |
limits.max_turns_per_run | integer | No | From 1 to 10. |
model | string | No | Up to 100 characters. |
name | string | Yes | Up to 80 characters. |
pool_ids | string[] | No | Up to 50 items. |
status | enum | No | One of: draft, shadow, live. Default "draft". |
tools | enum[] | No | Up to 20 items. |
Response 201
| Field | Type | Description |
|---|---|---|
created_at | string (date-time) | ISO 8601 timestamp. |
handoff_rules | object | |
handoff_rules.keywords | string[] | Hand off, without asking the model, when the contact’s message has one of these. Up to 50 items. |
handoff_rules.max_turns | integer | From 1 to 100. |
handoff_rules.on_low_confidence | boolean | Hand off when the model answers with neither text nor a tool call. Default true. |
handoff_rules.team_id | string (uuid) or null | Team the conversation is assigned to on handoff. |
handoff_rules.user_id | string (uuid) or null | Person the conversation is assigned to on handoff. |
id | string (uuid) | |
instructions | string | |
limits | object | |
limits.max_messages_per_conversation | integer | From 1 to 100. |
limits.max_runs_per_conversation_per_day | integer | From 1 to 200. |
limits.max_tokens_per_run | integer | From 1 to 200000. |
limits.max_tool_calls_per_run | integer | From 1 to 20. |
limits.max_turns_per_run | integer | From 1 to 10. |
model | string | |
model_fallback | boolean | |
model_in_use | object or null | |
model_in_use.id | string | |
model_in_use.name | string | |
name | string | |
pool_ids | string[] | |
runs_7d | object | |
runs_7d.conversations | integer | From 0 to 9007199254740991. |
runs_7d.error | integer | From 0 to 9007199254740991. |
runs_7d.handoff | integer | From 0 to 9007199254740991. |
runs_7d.resolved | integer | From 0 to 9007199254740991. |
runs_7d.total | integer | From 0 to 9007199254740991. |
status | enum | One of: draft, shadow, live, paused. |
tools | enum[] | |
updated_at | string (date-time) | ISO 8601 timestamp. |
usable_tools | enum[] |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Get an AI agent
GET /v1/agents/:id
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
Response 200
| Field | Type | Description |
|---|---|---|
created_at | string (date-time) | ISO 8601 timestamp. |
handoff_rules | object | |
handoff_rules.keywords | string[] | Hand off, without asking the model, when the contact’s message has one of these. Up to 50 items. |
handoff_rules.max_turns | integer | From 1 to 100. |
handoff_rules.on_low_confidence | boolean | Hand off when the model answers with neither text nor a tool call. Default true. |
handoff_rules.team_id | string (uuid) or null | Team the conversation is assigned to on handoff. |
handoff_rules.user_id | string (uuid) or null | Person the conversation is assigned to on handoff. |
id | string (uuid) | |
instructions | string | |
limits | object | |
limits.max_messages_per_conversation | integer | From 1 to 100. |
limits.max_runs_per_conversation_per_day | integer | From 1 to 200. |
limits.max_tokens_per_run | integer | From 1 to 200000. |
limits.max_tool_calls_per_run | integer | From 1 to 20. |
limits.max_turns_per_run | integer | From 1 to 10. |
model | string | |
model_fallback | boolean | |
model_in_use | object or null | |
model_in_use.id | string | |
model_in_use.name | string | |
name | string | |
pool_ids | string[] | |
runs_7d | object | |
runs_7d.conversations | integer | From 0 to 9007199254740991. |
runs_7d.error | integer | From 0 to 9007199254740991. |
runs_7d.handoff | integer | From 0 to 9007199254740991. |
runs_7d.resolved | integer | From 0 to 9007199254740991. |
runs_7d.total | integer | From 0 to 9007199254740991. |
status | enum | One of: draft, shadow, live, paused. |
tools | enum[] | |
updated_at | string (date-time) | ISO 8601 timestamp. |
usable_tools | enum[] |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Edit an AI agent, or turn it on, put it in shadow or pause it
PATCH /v1/agents/:id
Changing its tools or pools makes you the member whose role caps them. A new model must be one of GET /v1/agent-models; sending the agent’s current model, even one that was turned off, keeps it. Moving a live agent to any other status hands its conversations back to people. A paused organisation can still pause an agent. Signed-in members only: API keys are refused.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
idempotency-key | string | No | Makes the write safe to retry for 24 hours. 1 to 255 printable characters. Keys starting with outreach:, workflow: or agent: are reserved for messages bubbl3 sends itself and are refused with 400. Up to 255 characters. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
handoff_rules | object | No | |
handoff_rules.keywords | string[] | No | Hand off, without asking the model, when the contact’s message has one of these. Up to 50 items. |
handoff_rules.max_turns | integer | No | From 1 to 100. |
handoff_rules.on_low_confidence | boolean | No | Hand off when the model answers with neither text nor a tool call. Default true. |
handoff_rules.team_id | string (uuid) or null | No | Team the conversation is assigned to on handoff. |
handoff_rules.user_id | string (uuid) or null | No | Person the conversation is assigned to on handoff. |
instructions | string | No | Up to 20000 characters. |
limits | object | No | |
limits.max_messages_per_conversation | integer | No | From 1 to 100. |
limits.max_runs_per_conversation_per_day | integer | No | From 1 to 200. |
limits.max_tokens_per_run | integer | No | From 1 to 200000. |
limits.max_tool_calls_per_run | integer | No | From 1 to 20. |
limits.max_turns_per_run | integer | No | From 1 to 10. |
model | string | No | Up to 100 characters. |
name | string | No | Up to 80 characters. |
pool_ids | string[] | No | Up to 50 items. |
status | enum | No | One of: draft, shadow, live, paused. |
tools | enum[] | No | Up to 20 items. |
Response 200
| Field | Type | Description |
|---|---|---|
created_at | string (date-time) | ISO 8601 timestamp. |
handoff_rules | object | |
handoff_rules.keywords | string[] | Hand off, without asking the model, when the contact’s message has one of these. Up to 50 items. |
handoff_rules.max_turns | integer | From 1 to 100. |
handoff_rules.on_low_confidence | boolean | Hand off when the model answers with neither text nor a tool call. Default true. |
handoff_rules.team_id | string (uuid) or null | Team the conversation is assigned to on handoff. |
handoff_rules.user_id | string (uuid) or null | Person the conversation is assigned to on handoff. |
id | string (uuid) | |
instructions | string | |
limits | object | |
limits.max_messages_per_conversation | integer | From 1 to 100. |
limits.max_runs_per_conversation_per_day | integer | From 1 to 200. |
limits.max_tokens_per_run | integer | From 1 to 200000. |
limits.max_tool_calls_per_run | integer | From 1 to 20. |
limits.max_turns_per_run | integer | From 1 to 10. |
model | string | |
model_fallback | boolean | |
model_in_use | object or null | |
model_in_use.id | string | |
model_in_use.name | string | |
name | string | |
pool_ids | string[] | |
runs_7d | object | |
runs_7d.conversations | integer | From 0 to 9007199254740991. |
runs_7d.error | integer | From 0 to 9007199254740991. |
runs_7d.handoff | integer | From 0 to 9007199254740991. |
runs_7d.resolved | integer | From 0 to 9007199254740991. |
runs_7d.total | integer | From 0 to 9007199254740991. |
status | enum | One of: draft, shadow, live, paused. |
tools | enum[] | |
updated_at | string (date-time) | ISO 8601 timestamp. |
usable_tools | enum[] |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.