AI Agents API
An AI Agent is the brain behind your bot: its instructions, personality, language, knowledge and tools. You build an Agent once and then point traffic at it. This guide covers everything you can do with an Agent over the API — create it, configure it, give it knowledge and tools, review its drafts, and route conversations to it.
- Base URL —
https://api.youraiconnector.com/v1 - Authentication — your API key (see Authentication)
- Errors & paging — see Errors & Pagination
All examples below show the ?apiKey= query form in cURL and the X-API-Key header in JavaScript and Python — either works on every endpoint.
If you are new to Agents as a concept, read AI Agents first.
How an Agent fits together
Four things are managed separately, and it helps to know which is which before you start:
| Piece | What it is | Where you set it |
|---|---|---|
| Configuration | Instructions, rules, goal, personality, language, AI tier, booking and follow-up behaviour | PUT /agents/{agentId} or the narrower PUT /agents/{agentId}/bot-config |
| Knowledge | FAQs and knowledge sources (pages and documents the platform has read for you) | FAQs API and POST /agents/{agentId}/kb-sources |
| Tools | Custom functions and MCP servers the Agent may call mid-conversation | POST /agents/{agentId}/custom-functions and POST /agents/{agentId}/mcp-servers |
| Routing | Which channels and conversations actually reach this Agent | Entry Points — PUT /entry-points/channel-defaults and POST /agents/{agentId}/entry-points |
A new Agent answers nobody until you route to it. Creating an Agent does not put it on a channel. That is the step most integrations miss — see Routing conversations to an Agent at the end of this page.
The Agent object
A full Agent document is large — several hundred kilobytes, mostly its FAQ list, its knowledge sources and any page content read from your website. Because of that, listing returns a short summary row per Agent when you ask for it:
{
"id": "ag7HkQ2ZpLxR3mNb",
"name": "Listing assistant",
"active": true,
"language": "en",
"goal": "Book a viewing",
"tags": [],
"anthropic_model": "standard",
"ai_speed": "balanced",
"enable_bookings": false,
"enable_follow_ups": true,
"faq_refs_count": 42,
"kb_source_refs_count": 3,
"created_at": 1700000000000,
"last_modified_at": 1700000000000
}
| Field | Type | Description |
|---|---|---|
id |
string | The Agent’s unique identifier. |
name |
string | null | Agent name, as shown in the dashboard. |
active |
boolean | null | Whether the Agent is currently allowed to reply. |
language |
string | null | Language the Agent replies in. |
goal |
string | null | What the Agent works towards, shortened to the first 200 characters (a trailing ellipsis means it was shortened). |
tags |
array | null | The Agent’s tagging rules. |
anthropic_model |
string | null | AI quality tier: standard, economy, max or mini. |
ai_speed |
string | null | How much reasoning the Agent applies before replying: fast, fast_thinker, balanced or thorough. |
enable_bookings |
boolean | null | Whether the Agent may book appointments. |
enable_follow_ups |
boolean | null | Whether the Agent sends follow-up messages. |
faq_refs_count |
integer | How many FAQs are in this Agent’s knowledge base. |
kb_source_refs_count |
integer | How many knowledge sources are linked to it. |
created_at |
integer | null | Creation time, epoch milliseconds. |
last_modified_at |
integer | null | Last change, epoch milliseconds. |
The full document adds everything else: instructions, rules, personality, availability, follow_up_config, the linked FAQ and knowledge-source lists, the generated prose blocks, and any run state (tag_generation, optimize_run).
Some responses also carry
substrate_campaign_id. It is an internal record kept on older accounts; you never need to act on it, and on newer accounts it isnullor absent.
List Agents
GET /agents — every Agent on the account, newest first.
This endpoint is not paged. By default each Agent comes back with its full configuration, which is heavy: a single Agent can reach 580 KB and a 64-Agent account over 3 MB. Pass view=summary for a short row per Agent instead, then read the one you want with Get an Agent.
Query parameters
| Parameter | Description |
|---|---|
view |
Set to summary for short rows. Any other value returns 400. Omit for full documents. |
fields |
Only applies together with view=summary. Comma-separated summary keys to keep, for example id,name,active. id is always included; unknown names are ignored. |
cURL
curl "https://api.youraiconnector.com/v1/agents?apiKey=YOUR_API_KEY&view=summary&fields=id,name,active"
JavaScript
const res = await fetch("https://api.youraiconnector.com/v1/agents?view=summary", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const { agents } = await res.json();
Python
import requests
res = requests.get(
"https://api.youraiconnector.com/v1/agents",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"view": "summary"},
)
agents = res.json()["agents"]
Response (200)
{
"success": true,
"agents": [
{ "id": "ag7HkQ2ZpLxR3mNb", "name": "Listing assistant", "active": true }
]
}
Create an Agent
POST /agents — only name is really needed; send whatever configuration you already know alongside it. A new Agent is active by default.
Request fields (all optional except name)
| Field | Type | Description |
|---|---|---|
name |
string | Agent name. |
active |
boolean | Whether it may reply straight away. Defaults to true. |
language |
string | Language the Agent replies in. |
instructions |
string | Primary instructions that steer how it talks to contacts. |
rules |
string | Hard rules it must always follow. |
goal |
string | The outcome it should work towards. |
personality |
string | Tone of voice and personality. |
availability |
object | Active hours per weekday — see Set active hours. |
ai_speed |
string | fast, fast_thinker, balanced or thorough. |
anthropic_model |
string | standard, economy, max or mini. |
scrape_urls |
string[] | Pages to read and build the Agent’s instructions from. |
Building an Agent from your website. Include scrape_urls and the platform reads those pages and writes the instructions for you. The response tells you whether that generation started, so you know whether to poll the Agent for progress.
cURL
curl -X POST "https://api.youraiconnector.com/v1/agents?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Listing assistant",
"language": "en",
"instructions": "Answer questions about our listings and book viewings.",
"goal": "Book a viewing"
}'
JavaScript
const res = await fetch("https://api.youraiconnector.com/v1/agents", {
method: "POST",
headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({
name: "Listing assistant",
scrape_urls: ["https://example.com", "https://example.com/faq"],
}),
});
const data = await res.json();
console.log(data.agent_id);
Python
res = requests.post(
"https://api.youraiconnector.com/v1/agents",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"name": "Listing assistant", "scrape_urls": ["https://example.com"]},
)
print(res.json()["agent_id"])
Response (201)
{
"success": true,
"agent_id": "ag7HkQ2ZpLxR3mNb",
"substrate_campaign_id": null,
"agent_generation_queued": true
}
agent_generation_queued is true when the platform started writing the instructions from the pages you supplied.
A 400 means the body was not a JSON object, a field was rejected, or the Agent exceeds the configuration size your plan allows. A 403 means the account is not allowed to use one of the settings you sent — for example an AI tier its account provider has not granted.
Get an Agent
GET /agents/{agentId}
Pass fields with a comma-separated list to get back only what you need, for example fields=name,active,goal. The id is always included, and names that do not exist on the Agent are ignored rather than rejected. Omit it to get the whole document.
cURL
curl "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb?apiKey=YOUR_API_KEY&fields=name,active,goal"
JavaScript
const res = await fetch("https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb?fields=name,active", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const { agent } = await res.json();
Python
res = requests.get(
"https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"fields": "name,active"},
)
agent = res.json()["agent"]
An Agent that does not exist on your account returns 404.
Update an Agent
PUT /agents/{agentId} — send only the fields you want to change; everything else is left untouched.
Nested settings can be addressed leaf by leaf with a dotted key, so "availability.monday" changes just Monday and leaves the rest of the week alone.
Notes
- To change which bookable event type the Agent books into, send
event_id(the event’s id, ornullto clear it). Sendevent_idswith an array to link several at once — the first becomes the primary and[]unlinks everything.event_idandevent_idsare mutually exclusive, and theeventfield itself cannot be written directly. enable_bookingsmust be a real boolean, andbooking_providermust be one ofdefault,zenchef,formitable.- Ownership and identity fields are ignored, as is internal run state (generation and optimisation progress).
- Routing is not set here. Use
PUT /entry-points/channel-defaultsto make the Agent the answerer for a channel,POST /agents/{agentId}/entry-pointsfor keyword and comment rules, andPATCH /agents/{agentId}/activeto pause or resume it.
cURL
curl -X PUT "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"instructions": "Answer questions about our listings and always offer a viewing.",
"anthropic_model": "standard"
}'
JavaScript
await fetch("https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb", {
method: "PUT",
headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ "availability.monday": { start_time: "09:00", end_time: "17:00" } }),
});
Python
requests.put(
"https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"goal": "Book a viewing within three messages"},
)
Response (200)
{ "success": true, "agent_id": "ag7HkQ2ZpLxR3mNb" }
An empty body returns 400 with "No fields to update".
Update bot settings
PUT /agents/{agentId}/bot-config — the narrow way to change just the conversational settings.
An Agent has no separate bot section: its settings sit directly on the Agent, so the field names here are the same ones you would send to PUT /agents/{agentId}. This endpoint exists as the safe, focused way to change a handful of them. At least one field is required.
| Field | Description |
|---|---|
instructions |
Primary instructions that steer how the Agent talks to contacts. |
rules |
Hard rules it must always follow. |
goal |
The outcome it should work towards in each conversation. |
personality |
Tone-of-voice and personality description. |
language |
Language the Agent replies in. |
ai_speed |
fast, fast_thinker, balanced or thorough. |
anthropic_model |
standard, economy, max or mini. |
max_messages |
Maximum number of Agent messages per conversation. |
alert_human_when |
When the Agent should alert a human teammate. |
ai_transparency |
Whether the Agent discloses that it is an AI. |
Field names must be plain names here — letters, numbers, underscores and hyphens. Dotted paths are not accepted on this endpoint (unlike
PUT /agents/{agentId}), sobot.goalis rejected with a400.
curl -X PUT "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/bot-config?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "goal": "Book a viewing within three messages", "ai_speed": "thorough" }'
Long text counts against the configuration size your plan allows, so a very large instruction set can be refused with a 400.
Set active hours
PUT /agents/{agentId}/active-hours — the hours during which the Agent replies automatically. Outside those windows it stays quiet.
Send an availability object keyed by weekday (monday through sunday). Each day takes a single time window or a list of windows, in 24-hour HH:MM form. Days you leave out keep whatever they had, and any key that is not a weekday is rejected — so a typo cannot silently do nothing.
curl -X PUT "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/active-hours?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"availability": {
"monday": { "start_time": "09:00", "end_time": "17:00" },
"tuesday": [
{ "start_time": "09:00", "end_time": "12:00" },
{ "start_time": "13:00", "end_time": "17:00" }
]
}
}'
Response (200)
{ "success": true, "agent_id": "ag7HkQ2ZpLxR3mNb" }
A bad weekday key returns 400: "Invalid availability keys: funday. Allowed keys: monday through sunday."
Pause or resume an Agent
PATCH /agents/{agentId}/active — turns the Agent on or off. A paused Agent keeps all of its configuration but stops replying immediately; resuming takes effect straight away.
curl -X PATCH "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/active?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "active": false }'
await fetch("https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/active", {
method: "PATCH",
headers: { "X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ active: false }),
});
Response (200)
{ "success": true, "agent_id": "ag7HkQ2ZpLxR3mNb", "active": false }
active must be a real boolean — anything else returns 400 with "active (boolean) is required".
Duplicate an Agent
POST /agents/{agentId}/duplicate — creates a copy with its configuration preserved. The copy sends nothing until you point a channel or an Entry Point at it.
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/duplicate?apiKey=YOUR_API_KEY"
Response (201)
{ "success": true, "agent_id": "ag9WsX3cRfV6tGyH", "source_agent_id": "ag7HkQ2ZpLxR3mNb" }
A duplicate counts against your plan’s Agent allowance exactly like creating one from scratch, so it is refused with 403 when the account is at its limit.
Delete an Agent
DELETE /agents/{agentId}
The delete is refused while the Agent is still attached to something that would stop working without it — a broadcast, an Entry Point, or (on older accounts) a campaign. The response lists what is holding it so you can detach those first and retry.
curl -X DELETE "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb?apiKey=YOUR_API_KEY"
Response (200)
{ "success": true, "agent_id": "ag7HkQ2ZpLxR3mNb" }
Blocked (409)
{
"success": false,
"error": "Agent is still attached to one or more broadcast(s). Detach it first.",
"blocking_campaign_ids": [],
"blocking_broadcast_ids": ["bc5TgYhUj8IkOlPm"],
"blocking_entry_point_ids": []
}
Drafts: review changes before they go live
Edits made in the editor, and any rewrite produced by Optimize with AI, are held as an unpublished draft until you publish them. The live Agent keeps answering with its current configuration until then.
Publish the draft
POST /agents/{agentId}/publish-draft — moves the draft onto the live configuration and clears the draft in the same step.
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/publish-draft?apiKey=YOUR_API_KEY"
Response (200)
{ "success": true, "agent_id": "ag7HkQ2ZpLxR3mNb", "published_keys": ["instructions", "goal"] }
published_keys lists the settings that moved from the draft onto the live Agent, so you can show what changed.
Check that a draft exists before calling this. Publishing an Agent that has no draft is not a supported call and currently comes back as a
500with a generic message, not a specific one. To throw a draft away instead, use discard below.
Discard the draft
POST /agents/{agentId}/discard-draft — throws the draft away and leaves the live configuration exactly as it is. Safe to call when there is no draft; nothing happens.
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/discard-draft?apiKey=YOUR_API_KEY"
Optimize an Agent with AI
POST /agents/{agentId}/optimize — rewrites the Agent’s configuration from your feedback (“it keeps offering discounts”, “answers are too long”) and saves the rewrite as a draft rather than putting it live.
Send either user_feedback (a plain instruction) or, when reacting to a specific bad reply, thumbs_down_feedback together with the offending thumbs_down_message. At least one of the two must carry text.
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/optimize?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "user_feedback": "Keep replies under three sentences." }'
Response (202)
{ "success": true, "agent_id": "ag7HkQ2ZpLxR3mNb" }
The work runs in the background and the call returns straight away. Read the Agent with GET /agents/{agentId} and watch optimize_run.status; once it is back to Draft, the rewrite is waiting as the Agent’s draft. Review it, then either publish it or discard it.
Only one run at a time per Agent — a second call while one is in flight returns 409. This uses AI credits.
Tagging rules
A tagging rule is a tag plus a description of when it applies. During a conversation the Agent reads that description and tags the contact when it fits, which is how tag-driven automations get triggered.
The rule object
| Field | Required | Description |
|---|---|---|
name |
Yes | The tag to apply, for example hot-lead. |
description |
No | When the Agent should apply it, written as an instruction it follows. |
webhook |
No | URL called when the Agent applies this tag. |
ai_can_remove |
No | Whether the Agent may also take the tag off again. Defaults to false. |
tag_id |
No | Id of an existing tag on your account to link the rule to. Without it, the rule links to the tag with the same name, creating it if there is none — so every rule can be addressed by tag id afterwards. |
Add a tagging rule
POST /agents/{agentId}/tags
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/tags?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tag": {
"name": "hot-lead",
"description": "Apply when the contact asks about pricing or wants to book a call.",
"ai_can_remove": false
}
}'
Response (200)
{ "success": true, "agent_id": "ag7HkQ2ZpLxR3mNb", "tag": { "name": "hot-lead", "...": "..." } }
Replace a tagging rule
PUT /agents/{agentId}/tags/{tagId} — the rule is found by the tag id in the path and replaced wholesale, not merged, so send the full rule rather than only the part you are changing. The tag it points at is preserved even if you leave tag_id out, so an edit cannot detach the rule from its tag.
curl -X PUT "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/tags/tg8YuIoP2aSdF3gH?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "tag": { "name": "hot-lead", "description": "Apply only when the contact asks to book a call." } }'
Remove a tagging rule
DELETE /agents/{agentId}/tags/{tagId} — the Agent stops applying that tag. The tag itself, and any contacts already carrying it, are untouched.
curl -X DELETE "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/tags/tg8YuIoP2aSdF3gH?apiKey=YOUR_API_KEY"
Both endpoints return 404 when the Agent does not exist or when it has no rule for that tag.
Generate a tag set with AI
POST /agents/{agentId}/tags/generate — designs a whole set of rules (the tag names and the “apply when…” wording behind each) by reading the Agent’s own instructions and goal.
| Field | Description |
|---|---|
mode |
merge (the default) keeps the rules already on the Agent and adds to them. replace designs the set from scratch. |
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/tags/generate?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "mode": "merge" }'
Response (202)
{ "success": true, "agent_id": "ag7HkQ2ZpLxR3mNb", "mode": "merge" }
The work runs in the background. Read the Agent and watch tag_generation.status; the rules themselves land in the Agent’s tags. Only one run at a time per Agent (409 otherwise), and it uses AI credits.
Knowledge sources
Knowledge sources are the pages and documents the platform has read for you. Attaching one to an Agent lets it answer from that content.
Where source ids come from. Add content with the knowledge-base endpoints — POST /kb-sources/url for a page, POST /kb-sources/file for a document, POST /kb-sources/bulk-import for a whole site. Those return a source_id you poll with GET /kb-sources/{sourceId} until it is ready. POST /kb-sources/url also takes autoLinkToAgentId, which attaches the source to an Agent as soon as the import finishes, so you can skip the attach call below.
Attach knowledge sources
POST /agents/{agentId}/kb-sources — send kb_source_ids with a list to attach a whole set in one call (what you want after crawling a site), or kb_source_id for a single one. Send one or the other. Attaching something already attached changes nothing.
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/kb-sources?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "kb_source_ids": ["kb2QwErTyUi9OpAs", "kb6ZxCvBnM4kLjHg"] }'
Response (200)
{
"success": true,
"agent_id": "ag7HkQ2ZpLxR3mNb",
"kb_source_id": "kb2QwErTyUi9OpAs",
"kb_source_ids": ["kb2QwErTyUi9OpAs", "kb6ZxCvBnM4kLjHg"]
}
Detach knowledge sources
DELETE /agents/{agentId}/kb-sources/{kbSourceId} for one, or POST /agents/{agentId}/kb-sources/bulk-remove with kb_source_ids for several. Bulk removal is a POST because the list of ids travels in the body.
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/kb-sources/bulk-remove?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "kb_source_ids": ["kb2QwErTyUi9OpAs"] }'
The sources themselves are not deleted and stay available to your other Agents. Detaching something that is not attached changes nothing.
FAQs
FAQs are managed on their own endpoints and linked to an Agent from there: POST /faqs/{faqId}/link with { "agent_id": "ag7HkQ2ZpLxR3mNb" }, and POST /faqs/{faqId}/unlink to take it away again. A FAQ can be shared by any number of Agents. See the FAQs API.
A FAQ is only used by the Agents it is linked to — creating one is not enough on its own.
Tools
Custom functions
POST /agents/{agentId}/custom-functions lets the Agent call one of your custom functions during conversations. Only functions belonging to the same account can be attached, and attaching one that is already attached changes nothing.
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/custom-functions?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "custom_function_id": "cf7Hk2ZpLxR3mNbV" }'
DELETE /agents/{agentId}/custom-functions/{customFunctionId} detaches it. The function itself is not deleted and stays available to your other Agents.
Manage the functions themselves on /custom-functions — see Custom Functions for what they are.
MCP servers
An MCP server is a ready-made bundle of tools your Agent can discover and call on its own — see Connect MCP Servers to Your Bot. Servers are registered once on the account, then attached to whichever Agents should use them.
MCP servers need the custom functions feature on your plan. Without it, the account-level
/mcp-serversendpoints return403. Attaching an already-registered server to an Agent is not gated.
Register a server
POST /mcp-servers
| Field | Required | Description |
|---|---|---|
name |
Yes | A label for the server. |
url |
Yes | The server’s address. Must be reachable over the public internet. |
auth_type |
No | header (the default) for a static auth header, or oauth2. |
auth_header_name |
No | Header to send the credential in. Defaults to Authorization. |
auth_header_value |
No | The credential itself. Never returned in any response. |
enabled |
No | Whether the server is available to Agents. Defaults to true. |
enabled_tools |
No | Allow-list of tool names. null means every tool the server offers is on. |
tool_policies |
No | Per-tool limits, keyed by tool name — how often a tool may fire, result caching, and a read-only override. Pass null to clear them all. |
curl -X POST "https://api.youraiconnector.com/v1/mcp-servers?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Inventory",
"url": "https://tools.example.com/mcp",
"auth_header_value": "Bearer sk_live_xxx"
}'
Response (201)
{
"success": true,
"server_id": "ms4TgBnH7yUj2kLp",
"tools": [{ "name": "check_stock", "description": "Look up stock for a SKU." }],
"last_error": null,
"server": { "server_id": "ms4TgBnH7yUj2kLp", "name": "Inventory", "...": "..." }
}
On save, the platform connects to the server and caches the list of tools it offers. A server that cannot be reached still saves, with the reason in last_error and an empty tool list — so you can register first and fix connectivity afterwards.
An auth_type of oauth2 saves the registration with oauth_connected: false and no tools: there is no token yet. Authorising an OAuth server needs a browser sign-in and is done from the dashboard, not over the API.
List, update and delete servers
GET /mcp-servers— every registered server, newest first, underservers.PUT /mcp-servers/{serverId}— send only what you want to change. Changing the URL or the auth fields re-tests the connection and refreshes the cached tool list.DELETE /mcp-servers/{serverId}— removes the registration and unlinks it from every Agent and campaign that had it enabled.
curl "https://api.youraiconnector.com/v1/mcp-servers?apiKey=YOUR_API_KEY"
Secrets never come back. Responses carry auth_header_value_set (a true/false flag saying a value is stored) instead of the credential, and OAuth tokens and client secrets stay server-side. Everything else is returned: name, url, enabled, auth_type, auth_header_name, tools, enabled_tools, tool_policies, oauth_connected, tools_cached_at, last_connected_at, last_error, created_at, updated_at.
Test a connection
POST /mcp-servers/test-connection — connects to a server and lists its tools. Two ways to call it:
- with
server_id— tests the saved configuration and refreshes its cached tool list; - with an inline
url(plusauth_header_name/auth_header_value) — a pre-save test that stores nothing.
curl -X POST "https://api.youraiconnector.com/v1/mcp-servers/test-connection?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://tools.example.com/mcp", "auth_header_value": "Bearer sk_live_xxx" }'
Response (200)
{
"success": true,
"server_name": "Inventory tools",
"tools": [{ "name": "check_stock", "description": "Look up stock for a SKU." }]
}
A connection failure is not an HTTP error — you get a 200 with success: false and an error describing what went wrong, so you can show it next to the field the operator is editing.
Attach a server to an Agent
Registering a server does not give any Agent access to it. Attach it:
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/mcp-servers?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "mcp_server_id": "ms4TgBnH7yUj2kLp" }'
Response (200)
{ "success": true, "agent_id": "ag7HkQ2ZpLxR3mNb", "mcp_server_id": "ms4TgBnH7yUj2kLp" }
DELETE /agents/{agentId}/mcp-servers/{mcpServerId} detaches it again. The server itself is not deleted and stays available to your other Agents. Attaching or detaching something that is already in that state changes nothing.
Media library
The media library holds the files an Agent may send during a conversation — a menu, a price list, a product photo. An Agent can hold at most 50 items.
List media
GET /agents/{agentId}/media-library
curl "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/media-library?apiKey=YOUR_API_KEY"
Response (200)
{
"success": true,
"agent_id": "ag7HkQ2ZpLxR3mNb",
"media_items": [
{
"id": "mi4RtY7uIoP1aSdF",
"item_id": "mi4RtY7uIoP1aSdF",
"media_home": "agent",
"title": "Spring menu",
"description": "Send when someone asks what is on the menu.",
"ai_description": "A one-page menu listing seasonal dishes and prices.",
"type": "document",
"media_content_type": "application/pdf",
"media_url": "https://storage.googleapis.com/...",
"max_sends_per_conversation": 1,
"created_at": 1700000000000
}
]
}
Items stored on the Agent come first, then any older items still stored on the campaign the Agent was built from; media_home (agent or campaign) says which is which. Within each group the newest comes first.
media_urlexpires after 7 days. It is the download link created when the file was uploaded — treat an old one as stale rather than broken, and re-read the list to get a fresh link.
Upload media
POST /agents/{agentId}/media-library — the file is uploaded inline as base64, up to 10 MB. The call returns once the file is stored, so allow a little longer than for a normal request. Note that this body uses camelCase field names.
| Field | Required | Description |
|---|---|---|
base64Data |
Yes | File contents, base64 encoded, without a data-URL prefix. |
mimeType |
Yes | MIME type of the file. |
fileName |
Yes | Original filename, used to name the stored file. |
title |
No | Short label shown in the library. |
description |
No | The “when should the Agent send this” instruction. |
sendMessage |
No | Preferred wording the Agent says when it sends the item. Trimmed to 500 characters. |
maxSendsPerConversation |
No | How many times it may be sent to the same contact in one conversation. Defaults to 1. |
sendAsVoiceNote |
No | Audio uploads only — store the file as a WhatsApp voice note. Ignored for other file types. |
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/media-library?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"base64Data": "JVBERi0xLjQKJcfs...",
"mimeType": "application/pdf",
"fileName": "spring-menu.pdf",
"title": "Spring menu",
"description": "Send when someone asks what is on the menu.",
"maxSendsPerConversation": 1
}'
Two things happen automatically: an animated GIF is converted to video so it plays on every channel, and the platform writes a short summary of what is actually in the file so the Agent knows when it fits.
A 400 covers missing fields, an unsupported file type, an empty or oversized file, and hitting the 50-item limit. A 403 means the media library is switched off for the account.
Update a media item
PATCH /agents/{agentId}/media-library/{itemId} — metadata only. The file itself cannot be replaced; upload a new item and delete the old one. This body uses snake_case: title, description, send_message, max_sends_per_conversation (a non-negative whole number, or null to clear the limit).
curl -X PATCH "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/media-library/mi4RtY7uIoP1aSdF?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "title": "Summer menu", "max_sends_per_conversation": 2 }'
Response (200)
{
"success": true,
"agent_id": "ag7HkQ2ZpLxR3mNb",
"item_id": "mi4RtY7uIoP1aSdF",
"campaign_id": "",
"media_home": "agent"
}
Delete a media item
DELETE /agents/{agentId}/media-library/{itemId} — removes the item and its stored file. Deleting an item that is already gone succeeds and reports deleted: false, so the call is safe to retry.
curl -X DELETE "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/media-library/mi4RtY7uIoP1aSdF?apiKey=YOUR_API_KEY"
Generate follow-up messages
POST /agents/{agentId}/template-generation — writes the Agent’s follow-up messages for you (the nudges it sends when a conversation goes quiet), based on what the Agent is for.
| Field | Description |
|---|---|
type |
all (the default) writes the whole set. cold_only writes only the messages for contacts who never replied. |
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/template-generation?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "all" }'
There are two ways this comes back, and the target field tells you which:
target: "agent"with a200— the messages were written during the call and the result is indata. Read them back from the Agent’sfollow_up_config. This is the usual case.target: "campaign"with a202— the work was queued against the campaign named incampaign_id. Watch that campaign’stemplate_generation_statusuntil it finishes.
cold_only needs an outgoing campaign and is refused with 409 (reason: "cold_only_requires_campaign") on an Agent that has none. A 403 means automatic follow-ups are not switched on for the account. This uses AI credits, and a 400 with "Insufficient credits." means the account is out.
Routing conversations to an Agent
An Agent only answers the conversations an Entry Point sends it. Until a channel has one, a first message from someone you have never spoken to is still stored, but nothing picks it up and no assistant replies.
| What you want to do | Call |
|---|---|
| Make an Agent the answerer for a whole channel | PUT /entry-points/channel-defaults with { "channel": "instagram", "agent_id": "AGENT_ID" } |
| Add a narrower rule (keywords, comments, new followers) | POST /agents/{agentId}/entry-points |
| See the rules pointing at one Agent | GET /agents/{agentId}/entry-points |
| Leave a channel with nobody answering | DELETE /entry-points/channel-defaults?channel=instagram |
List an Agent’s Entry Points
GET /agents/{agentId}/entry-points — the routing rules that send conversations to this Agent, newest first. Both current and retired rules come back; a retired one has enabled: false.
curl "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/entry-points?apiKey=YOUR_API_KEY"
For the whole account’s channel defaults, including a channel deliberately set to nobody, read GET /entry-points/channel-defaults instead.
Create an Entry Point
POST /agents/{agentId}/entry-points — the Agent in the path always wins, so a rule can never be created for a different Agent than the one in the URL.
type |
What it does |
|---|---|
channel_default |
The Agent answers every new contact on the listed channels. Prefer PUT /entry-points/channel-defaults for this — it retires the previous answerer for you, which creating a second default here does not. |
keyword |
The Agent takes over when the first message contains one of match_config.keywords. At least one keyword is required. |
instagram_comment / facebook_comment |
The Agent replies to comments on your posts. The matching channel must be listed in channels. |
instagram_follower |
The Agent greets new followers. |
channels is required and says which channels the rule covers — for example whatsapp, whatsapp_web, instagram, messenger, telegram, sms, email, chat_widget or custom_channel. New rules are enabled unless you say otherwise.
curl -X POST "https://api.youraiconnector.com/v1/agents/ag7HkQ2ZpLxR3mNb/entry-points?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "keyword",
"channels": ["whatsapp", "instagram"],
"match_config": { "keywords": ["pricing", "quote"] }
}'
Response (201)
{ "success": true, "entry_point_id": "ep3KmQ8vTzXr5nWd" }
Which rule wins when several could: an ongoing conversation or a manual assignment keeps the Agent it already has; otherwise keyword rules beat comment rules, which beat follower rules, and a channel default is the last resort. Whether these rules decide anything yet on an account is reported by GET /entry-points/routing-status.
This is the short version. The Entry Points API guide covers the full ladder, comment and follower rules, one Agent per WhatsApp number, and changing or deleting a rule. See Entry Points for the concept, and the Channels API for connecting the channel itself.
AI Agents API errors
Agent endpoints return the standard error envelope:
{
"success": false,
"error": "Agent not found"
}
| Status | When it happens on an Agent endpoint |
|---|---|
400 |
A required field is missing or invalid — an empty update body, a value outside an allowed list (ai_speed, anthropic_model, booking_provider, mode, type), a non-weekday key in availability, a dotted field name on bot-config, or a malformed id in the path. |
403 |
The account is not allowed to use a setting you sent, you are at your plan’s Agent limit, or a feature this endpoint needs (media library, follow-ups, custom functions for MCP servers) is off. A change that exceeds the configuration size your plan allows is refused with 400. |
404 |
The Agent, tag rule, media item or MCP server was not found — either it does not exist or it belongs to another account. |
409 |
Something is already in flight or in the way: an optimisation or tag generation is running, the Agent is still attached to a broadcast, Entry Point or campaign, or cold_only was asked for without an outgoing campaign. |
The shared codes every endpoint can return — 401, 403 (your plan does not include API access), 429 (rate limit) and 500 — are listed with retry guidance in Errors & Pagination.
A note on the explorer. The
/agentsendpoints are in the published OpenAPI specification, so you can browse their exact fields and run live requests in the API Reference. The account-level/mcp-serversendpoints are in the specification too, so you can explore them there as well.
Related
- AI Agents — what an Agent is, in plain language.
- Entry Points — how conversations get routed to an Agent.
- FAQs API — build and link the knowledge your Agent answers from.
- Channels API — connect the channels an Agent answers on.
- Connect MCP Servers to Your Bot · Custom Functions
- API Reference — the full interactive endpoint explorer.