Your AI Connector Docs

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.

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 is null or 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, or null to clear it). Send event_ids with an array to link several at once — the first becomes the primary and [] unlinks everything. event_id and event_ids are mutually exclusive, and the event field itself cannot be written directly.
  • enable_bookings must be a real boolean, and booking_provider must be one of default, 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-defaults to make the Agent the answerer for a channel, POST /agents/{agentId}/entry-points for keyword and comment rules, and PATCH /agents/{agentId}/active to 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}), so bot.goal is rejected with a 400.

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 500 with 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-servers endpoints return 403. 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, under servers.
  • 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 (plus auth_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_url expires 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 a 200 — the messages were written during the call and the result is in data. Read them back from the Agent’s follow_up_config. This is the usual case.
  • target: "campaign" with a 202 — the work was queued against the campaign named in campaign_id. Watch that campaign’s template_generation_status until 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 /agents endpoints 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-servers endpoints are in the specification too, so you can explore them there as well.