Kollie Logo
Microsoft for Startups
HubSpot solutions technology partner
Browse all articles

Build an agent with the API

Technical reference for building Kollie agents programmatically: endpoints, config keys, skill types, build order and security.

Last updated:

On this page

Technical reference for building agents programmatically. What to write in the fields is not covered here, and the rules are identical whichever way the agent is built. See How to build your AI phone agent.

Base URL: https://api.kollie.ai. The Swagger UI is at the same address.

Authentication

Generate your own API key on the platform under API Keys in the left-hand menu. That key authenticates every request. There is no token issuance step and nothing else to obtain.

Building an agent: two stages

Stage 1: create the agent

POST /api/v1/agents

{
  "name": "SALES-EN-DEMOBOOKING",
  "organization_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "config": { }
}

Returns the created agent including its id, which you need for stage 2.

Everything about the agent lives in config.

The three text fields:

config keyInterface field
initial_messageIntroduction
business_infoBusiness Information
main_objectiveMain Objective

These three carry all the prose, and all of it is governed by How to build your AI phone agent. Nothing relaxes because the configuration arrived as JSON rather than through a form.

Everything else in config is a setting. Set these; never describe them in the text fields.

config keyControls
languageVoice and language identifier. One per agent.
temperatureCreativity: how strictly the prompt is followed.
modelModel selection.
recordingCall recording.
notificationsEmail / SMS / WhatsApp delivery. Object with email, sms, whatsapp, each {enabled, from, to}.
hangup_on_voicemailVoicemail detection.
allow_agent_to_be_cut_offBarge-in: whether the caller can interrupt.
adaptive_interruption, transcriberInterruption detection and handling.
stt_keytermsIndustry Terms. Array of words given to the speech recogniser as a reference for anything that sounds ambiguous.
from_phone, vonage_app_idTelephony. Set by the phone-linking step, not here.
versionConfig schema version.
example_dialogLegacy, no longer used. Leave empty.

Stage 2: attach each skill

POST /api/v1/agents/{agent_id}/skills, once per skill. The same endpoint updates an existing skill's config.

{
  "type": "call_transfer",
  "enabled": true,
  "config": {
    "toPhone": "+3120XXXXX01",
    "description": "Sales team, showroom floor. Use this number when the caller is asking about getting a vehicle: availability, delivery times, a price or lease quote, trade-in value, financing, or booking a test drive."
  }
}

A skill that exists but is not enabled does nothing.

Skill types

Verified against a live agent carrying all of them.

typeexec_orderInterface nameconfig
apipre_callCall API — Before Callurl, method, qs
apipost_callCall API — After Callurl, method, qs
external_actionduring_callExternal Action / Call API — During Callname, description, url, method, params[], header, results[]
call_transfer—Call TransfertoPhone, description
scheduling—Book Meetingintegration_id
round_robin—Round Robindata.integrations[]
extract_information—Extract Informationdata[] of {type, name, description}
hubspotpost_callHubSpot Syncdata.integrations[], each {integration_id, contact_custom_fields, calls_custom_fields}
hubspot_contact_lookuppre_callGet Contact by Phone Numberintegration_id

Four things this table settles:

  • Call API Pre and Post are one type, api, distinguished only by exec_order. Not two skills.
  • external_action is the During Call mode and a genuinely separate type, with the extra fields that mode needs: an action name the agent invokes, a description it reads to decide when, params[] for the request and results[] for the response.
  • Get Contact by Attribute has no type of its own. It is an external_action pointing at the contacts search endpoint. Only the silent phone-number lookup has a dedicated type.
  • exec_order only means something for api and external_action, where it decides when the request fires. On the other types it is stored but carries no timing meaning: read nothing into a call_transfer showing post_call.

Which types have a description the agent reads: call_transfer, external_action, and each field inside extract_information. Those are the ones the writing rules apply to. The rest are endpoints and integration ids with nothing for the agent to interpret.

Industry Terms is not a skill. It is stt_keyterms in agent config.

Other endpoints

MethodPathUse
GET/api/v1/agentsList agents.
GET/api/v1/agents/{agent_id}Read one back, with its skills. The quickest way to see a working agent's real shape.
PUT/api/v1/agents/{agent_id}Update an agent.
DELETE/api/v1/agents/{agent_id}Delete an agent.
DELETE/api/v1/agents/{agent_id}/skills/{skill_id}Remove a skill (soft delete).
POST/api/v1/agents/{agent_id}/callTrigger an outbound call. Where trigger-time parameters for {{ placeholders }} are passed.
POST/api/v1/agents/{agent_id}/webcallTrigger a web call.
POST/api/v1/agents/{agent_id}/campaignCreate an outbound campaign.
GET/api/v1/agents/{agent_id}/callsCall history for the agent.

Build order

  1. POST /api/v1/agents with name, organization_id and a complete config.
  2. Take id from the response.
  3. POST /api/v1/agents/{id}/skills once per skill, each with enabled: true.
  4. GET /api/v1/agents/{id} and check what came back matches what you sent.
  5. Place a test call.

Driving this from a script or an LLM? The content rules in How to build your AI phone agent apply unchanged, and the automated pre-save check is written to be run as a validation step before step 1.

Security

Your API key is a secret. Keep it in an environment variable or a secrets manager: never in source control, never pasted into a ticket, never in a shared document. If a key has been shown to a model in conversation, treat it as exposed and rotate it. That matters here specifically: if an LLM is driving your script, it should drive the script, not hold the key.

Never put credentials in business_info, main_objective, or a skill description. Those are prompt content. Everything in them is read by the agent on every call, and anything the agent has read, it can be talked into saying.

Credentials a skill legitimately needs go in that skill's own config: on an external_action, the header object with its name and value. That is the correct place, and the only correct place.

But know what that means: skill credentials are readable. GET /api/v1/agents/{agent_id} returns each skill's config in full, header values in plaintext included. Anyone who can read an agent through the API or export its JSON can read the keys inside it. So:

  • Give each skill the narrowest key that works, never a shared or admin-scoped one.
  • Treat any exported agent JSON as a secret.
  • Strip header values before pasting a configuration into a ticket, a chat, a document, or a model.

Watch results[] on external_action. Leave it empty and the agent receives the entire API response: every field the endpoint returns, including internal ids and debug output. Filtering is opt-in. Map the fields you actually need and keep the list narrow: it costs one line of config and keeps attacker-controlled free text in unrelated fields from reaching the agent as content.

{{ placeholder }} tokens are not credentials. They are fill-in-the-blank markers for reusing a saved template with another customer. Never substitute a real key for one.

Related articles

Still need help?

Can't find the answer you're looking for? Send us a ticket and our support team will help you set up and improve your agents.

Submit a support ticket