agentcalls/SKILL.md

name: agentcalls

description: Run a business phone line through AgentCalls — AI voice agents on real phone numbers in 95+ countries. Use when the user wants to build or change a phone agent (receptionist, booking, lead qualification, support), get a phone number, make or receive calls, run an outbound calling campaign, read call transcripts and summaries, manage contacts, webhooks, integrations or billing through the AgentCalls MCP server or REST API. Covers the end-to-end workflow, the confirmation rules for money and irreversible actions, and how calls behave asynchronously.

AgentCalls

AgentCalls gives one organization AI voice agents that answer and place real phone calls. Through the MCP server (https://agentcalls.net/mcp, OAuth sign-in or API key) you can do everything the dashboard does: build and publish agents, get numbers, start and end calls, run campaigns, read transcripts and summaries, manage contacts, integrations, webhooks, outbound rules and billing.

Before anything else

  1. Call get_started. It returns the organization's state (plan, balance, agents, numbers, integrations, recent calls) and an ordered nextSteps list naming the tool for each step. Follow it rather than guessing.
  2. Read references/workflows.md for the step-by-step recipes and references/tools.md for every tool's arguments (generated from the server, always current).
  3. Keep the user in the loop: summarise what you found, propose the next step, and ask before anything that costs money or cannot be undone (see "Confirmation rules").

The mental model

  • Agent = a draft configuration (instructions, opening line, voice, hours, tools, analysis schema). Calls use the published version only; editing a draft changes nothing until publish_agent.
  • Number = a phone number owned by the organization. Inbound calls on a number go to the agent assigned to it (assign_number). Outbound calls (start_call, campaigns) need an active number that is not inbound-only.
  • Call = one conversation. Live statuses: queued, ringing, in_progress. Final: ended, no_answer, busy, voicemail, failed. Summary, outcome, sentiment and structured data arrive about a minute after hang-up (get_call); the transcript grows live (get_transcript).
  • Campaign = a list of contacts dialled by one agent from one number inside calling hours, with retries. Rows move queued → calling → done | failed | skipped.
  • Contact = one phone number with name, attributes, tags, notes, a memory summary the agent reads at the start of each call, and a do-not-call flag that blocks outbound calls.
  • Money: a plan includes minutes and numbers per period; beyond that, calls draw from a prepaid wallet. Numbers cost a monthly fee. get_usage shows all of it.

Confirmation rules (non-negotiable)

These tools spend the user's money, dial real phones, hang up on real people or cannot be undone. Each takes confirm: true; without it the server answers confirmation_required. Never set confirm: true unless the user said yes in this conversation to that specific action, after you showed them what it does and what it costs:

ToolWhy it needs a yes
buy_numberMonthly charge for the number
request_numberStarts a registration that ends in a monthly charge
release_numberIrreversible; callers can no longer reach the number
start_callDials a real phone; per-minute cost
end_callHangs up on a real person
create_campaign with start: "now" or "at", set_campaign_status → runningDials many real phones; cost scales with the list
set_campaign_status → cancelledIrreversible

A campaign saved as a draft and set_campaign_status → paused need no confirmation. Links from get_topup_link and get_plan_link charge nothing by themselves (the user pays on the hosted page), but say what the link is before sharing it.

Working style

  • Prefer one tool call that answers the question over several speculative ones. Lists are paginated with cursor; stop when you have enough.
  • Use the ids tools return (agentId, phoneNumberId, callId, campaignId, contactId); never invent ids. Phone numbers are E.164 (+14155550123).
  • Outputs are plain language (ended reasons, action labels); quote them as they are. Do not expose provider names or raw codes to the user.
  • When a tool fails, read error.code and error.message; most tell you the fix (publish the agent, allow the country, top up, acknowledge compliance in the dashboard). references/errors.md lists them.
  • Dashboard links in results (dashboardUrl) are for the user; offer them when a step needs a human (documents, compliance acknowledgement, payment).
  • Arabic organizations (organization.locale = "ar") expect Arabic replies and Arabic agents; voices and templates exist in both languages.

Typical requests → where to start

User saysStart with
"Set up a phone line / receptionist for my business"get_started, then the Set up a phone line recipe
"Call X about Y"get_contact (do-not-call?), list_agents (live), then start_call with a note after the yes
"What did callers want this week?"list_calls with direction: "inbound" and since, then get_call for details
"Remind all customers that…"Run a campaign recipe (draft first, review rejected rows, then start)
"Get me a number in …"list_number_countries → instant: search_numbers + buy_number; request: request_number
"Connect my calendar / CRM"list_integrations → get_integration_connect_link → update_agent tools
"Notify my system after each call"create_webhook with call.summary.ready
"How much have I spent / what is my balance?"get_usage, list_invoices

The prompts set_up_phone_line, design_receptionist_agent, review_recent_calls, place_follow_up_call and run_campaign package the recipes above for clients that support MCP prompts.

agentcalls/references/workflows.md

AgentCalls workflows

Step-by-step recipes. Every step names the tool; arguments are in tools.md. "Ask" means stop and get the user's answer before continuing.

1. Set up a phone line from scratch

  1. get_started. Note the plan: on the free trial, agents can be built and tested in the browser, but numbers and outbound calls need a paid plan (get_plan_link → the user subscribes on the hosted page).
  2. Pick a voice: list_voices with the agent's language; propose two (one standard, one premium — premium costs more per minute, see get_usage.rates) with their previewUrl. Ask which.
  3. Draft the agent: read the matching template resource (agentcalls://templates/{receptionist|appointment_booking|lead_qualification|support}/{en|ar}), adapt the instructions to the business (name, services, hours, what to book, what never to promise, when to transfer). Ask for missing facts. Then create_agent with template, name, language, voiceId, instructions, firstMessage, businessHours, and transferNumber if a human should take hand-offs.
  4. Show the draft (the agent card renders it). Iterate with update_agent (only the fields that change). When approved, publish_agent.
  5. Hear it: test_agent returns the in-browser test link (free trial minutes, no number needed). If an outbound number exists, offer to call the user's own phone (start_call after their yes, with a short note).
  6. Get a number: list_number_countries (or pass country). Instant countries: search_numbers (US/CA accept areaCode), show three candidates with the monthly price, and after the yes buy_number with agentId so the number routes to the agent at once. Request countries (UK, UAE, Saudi Arabia, Germany, …): collect fields (business name, registered address, contact, intended use), show them, and after the yes request_number; tell the user to upload the documents on the dashboard page in the result and that activation takes up to three business days (list_numbers shows open requests).
  7. Finish: assign_number if the number is not yet routed; suggest create_webhook (call.summary.ready) if the user has a system to notify; show get_usage.

2. Change an agent

  • get_agent first; change only what the user asked with update_agent; publish_agent so calls pick it up. Mention that the previous published version keeps taking calls until publish.
  • Give the agent tools: list_integrations (connected ones have status: "active"); pass action slugs as { kind: "integration", slug } plus the built-ins (transfer_call needs transferNumber, end_call, update_contact, lookup_contact) in update_agent.tools. Unconnected toolkits: get_integration_connect_link for the user to connect first.
  • Structured data after calls: analysisSchema (JSON Schema object). Summary focus: summaryPrompt.

3. Place one call

  1. get_contact by phone: if doNotCall is true, stop and tell the user.
  2. list_agents (status: "live") and list_numbers (an active number with inboundOnly: false). If several outbound numbers exist, pass phoneNumberId.
  3. Tell the user who will be called, by which agent, from which number, the goal and the per-minute rate (get_usage.rates). Ask.
  4. start_call with confirm: true, contactName if known, note = the goal, and an idempotencyKey when the request could be repeated. The call card shows live status, transcript and an End-call button; otherwise poll get_call every 20–30 s until live is false, then once more a minute later for the summary.
  5. Report outcome, sentiment, summary, duration and cost. get_recording_url gives a 10-minute link if the user wants to listen.
  6. If the user wants the call stopped: end_call after their yes.

4. Review inbound and outbound activity

  • list_calls with direction, since/until, status, agentId, contact or campaignId; follow nextCursor up to what is useful (≤ 200 calls for a summary).
  • Group by outcome and sentiment, count no-answer/busy/voicemail/failed, sum durationSec and costCents. Quote summary for the calls that matter; get_transcript only when the user asks what was said.
  • list_live_calls shows what is happening right now and the line usage.
  • Suggest concrete instruction improvements with evidence from calls.

5. Run an outbound campaign

  1. get_organization: outbound.acknowledged must be true, otherwise the user has to tick the compliance acknowledgement in the dashboard (Settings → Outbound calling). Check allowedCountries covers the list; update_outbound_settings can add countries after the user's yes.
  2. get_usage: enough included minutes or wallet for list size × expected minutes × rate; concurrency.limit must be ≥ 2 (one line is reserved for inbound).
  3. Collect the list (names + numbers, or CSV text with a header), the published agent and the caller-id number. Suggest a note-style goal in the agent instructions if needed (update_agent).
  4. create_campaign with start: "draft". Report accepted and every rejected row with its reason (invalid phone, duplicate, do-not-call, country not allowed); fix what the user wants.
  5. After the explicit yes: set_campaign_status → running with confirm: true (or start: "at" with startAt for later). The campaign card shows progress; otherwise poll get_campaign every minute.
  6. set_campaign_status → paused to stop dialling without confirmation; cancelled ends it for good (needs the yes).
  7. When completed: get_campaign_results, summarise outcomes, list contacts that need a human follow-up.

6. Contacts, memory and do-not-call

  • list_contacts (search by name, phone prefix or email; tag; doNotCall), get_contact (includes memorySummary and callCount).
  • upsert_contact creates or updates (attributes merge, tags replace). Attributes are visible to the agent during calls.
  • set_do_not_call blocks or unblocks outbound calls; it creates the contact if needed so the block sticks.

7. Webhooks and automation

  • create_webhook with events (call.started, call.ended, call.summary.ready, number.activated, wallet.low); the signing secret is shown once. test_webhook sends a sample. list_webhooks shows recent deliveries.
  • The REST API (https://agentcalls.net/docs) offers the same objects with API keys for servers.

8. Billing

  • get_usage: plan, included minutes left, wallet, numbers allowance, lines, rates, period spend.
  • get_topup_link (packs $25/$50/$100/$250) and get_plan_link (checkout for trial organizations, billing portal otherwise) return hosted pages for the user to pay on; nothing is charged by the tools.
  • list_invoices for history (hosted PDF links).

agentcalls/references/tools.md

Tools reference

Generated from the server's tool spec (packages/mcp-server/src/spec.ts); do not edit by hand. Every tool returns JSON (structuredContent) and, on failure, { error: { code, message, details? } }. Lists accept limit (≤ 100) and cursor.

Getting started

get_started — Where am I? (setup status and next steps)

Scope org:read · read-only, card.

Call this first in a new conversation. Returns the organization's setup state (plan, balance, agents, numbers, integrations, recent calls) and an ordered list of next steps, each naming the tool to call and the dashboard page to open. Nothing is changed.

Returns: { organization, plan, balance, agents, numbers, integrations, recentCalls, onboarding, nextSteps: [{ step, why, tool, dashboardUrl }] }.

No arguments.

Example:

{}

Agents and voices

list_agents — List agents

Scope agents:read · read-only.

List the organization's voice agents with id, name, language, status, published version and voice tier. Use status: "live" to find agents that can take calls.

Returns: agents: array of { id, name, language, status, publishedVersion, voiceTier, firstMessage, createdAt }.

ArgumentTypeRequiredDescription
status"all" | "live" | "draft" | "archived"default "all"Filter by lifecycle state. live = has a published version.

Example:

{
  "status": "live"
}

get_agent — Get an agent

Scope agents:read · read-only, card.

Full configuration of one agent (draft): instructions, opening line, voice, consent, transfer number, hours, analysis schema, tools and publish state.

Returns: The agent object with every editable field, the voice as a list_voices id, and publishedVersion (0 = never published).

ArgumentTypeRequiredDescription
agentIdstringyesAgent id.

Example:

{
  "agentId": "6ad0bf49171339b53c8e"
}

create_agent — Create an agent

Scope agents:write · card.

Create a draft voice agent from a template, overriding any field you pass (instructions, opening line, voice, hours, tools…). The configuration is validated with the voice platform before it is saved. The draft takes no calls until publish_agent.

Returns: The created agent (same shape as get_agent). Validation failures come back as an error listing the issues.

ArgumentTypeRequiredDescription
template"receptionist" | "appointment_booking" | "lead_qualification" | "support"default "receptionist"Starting point for everything you do not set: instructions, opening line, consent text, summary prompt, analysis schema, built-in contact tools.
namestringnoDisplay name of the agent, e.g. "Front desk".
language"en" | "ar"noLanguage the agent speaks and understands: en or ar. Changes the default voice and consent text.
voiceIdstringnoVoice id from list_voices (not a provider voice id). Defaults to the language's default voice.
instructionsstringnoSystem instructions in plain language: who the agent is, what it may do and say, what it must never do. Markdown allowed.
firstMessagestringnoThe agent's opening line when a call connects.
consentAnnouncementbooleannoPlay a recording announcement at the start of every call (default true; keep it on unless local law allows otherwise).
consentTextstringnoThe announcement text, in the agent's language.
transferNumberstring | nullnoHuman hand-off number for the transfer_call tool, or null to disable transfers.
maxDurationSecintegernoHard cap on call length in seconds (default 900).
silenceTimeoutSecintegernoSeconds of silence before the agent ends the call (default 20).
endCallPhrasesarray of stringnoPhrases that end the call when the caller says them.
voicemailDetectionbooleannoHang up (outbound) when voicemail answers instead of talking to the machine (default true).
memoryEnabledbooleannoGive the agent the contact's memory summary and let it update contact attributes (default true).
summaryPromptstring | nullnoExtra guidance for the post-call summary, or null for the default.
analysisSchemaobject | nullnoJSON Schema (object) of the structured data to extract after each call, e.g. { type: "object", properties: { appointment_confirmed: { type: "boolean" } } }. Null disables extraction.
businessHoursobject | nullnoOpening hours the agent reads to callers; null means always open.
toolsarray of object | objectnoReplaces the agent's tool list: built-in call tools and actions from connected integrations the agent may use during calls.

Example:

{
  "template": "receptionist",
  "name": "Front desk",
  "language": "en",
  "instructions": "You are Mia, the receptionist of Northwind Clinic…"
}

update_agent — Update an agent

Scope agents:write · card.

Change fields of an agent's draft. Only the fields you pass change. Published calls keep using the last published version until you call publish_agent again.

Returns: The updated draft.

ArgumentTypeRequiredDescription
agentIdstringyesAgent id.
namestringnoDisplay name of the agent, e.g. "Front desk".
language"en" | "ar"noLanguage the agent speaks and understands: en or ar. Changes the default voice and consent text.
voiceIdstringnoVoice id from list_voices (not a provider voice id). Defaults to the language's default voice.
instructionsstringnoSystem instructions in plain language: who the agent is, what it may do and say, what it must never do. Markdown allowed.
firstMessagestringnoThe agent's opening line when a call connects.
consentAnnouncementbooleannoPlay a recording announcement at the start of every call (default true; keep it on unless local law allows otherwise).
consentTextstringnoThe announcement text, in the agent's language.
transferNumberstring | nullnoHuman hand-off number for the transfer_call tool, or null to disable transfers.
maxDurationSecintegernoHard cap on call length in seconds (default 900).
silenceTimeoutSecintegernoSeconds of silence before the agent ends the call (default 20).
endCallPhrasesarray of stringnoPhrases that end the call when the caller says them.
voicemailDetectionbooleannoHang up (outbound) when voicemail answers instead of talking to the machine (default true).
memoryEnabledbooleannoGive the agent the contact's memory summary and let it update contact attributes (default true).
summaryPromptstring | nullnoExtra guidance for the post-call summary, or null for the default.
analysisSchemaobject | nullnoJSON Schema (object) of the structured data to extract after each call, e.g. { type: "object", properties: { appointment_confirmed: { type: "boolean" } } }. Null disables extraction.
businessHoursobject | nullnoOpening hours the agent reads to callers; null means always open.
toolsarray of object | objectnoReplaces the agent's tool list: built-in call tools and actions from connected integrations the agent may use during calls.

Example:

{
  "agentId": "6ad0bf49171339b53c8e",
  "firstMessage": "Thanks for calling Northwind Clinic, this is Mia."
}

publish_agent — Publish an agent

Scope agents:write · card.

Validate the draft (locally and with the voice platform) and publish it as a new immutable version. From this moment inbound calls on assigned numbers and start_call use the new version.

Returns: { agentId, version, status: "live" }, or an error listing blocking issues.

ArgumentTypeRequiredDescription
agentIdstringyesAgent id.

Example:

{
  "agentId": "6ad0bf49171339b53c8e"
}

test_agent — Test an agent

Scope agents:read · read-only.

Dry-run check of an agent: validates the draft with the voice platform and reports warnings, then tells you how to hear it: a link to the in-browser test call (free trial minutes, no phone needed) and, when the organization has an outbound number, how to call the user's own phone with start_call.

Returns: { agentId, ok, issues, browserTestUrl, phoneTest: { possible, phoneNumberId, note } }.

ArgumentTypeRequiredDescription
agentIdstringyesAgent id.

Example:

{
  "agentId": "6ad0bf49171339b53c8e"
}

list_voices — List voices

Scope voices:read · read-only.

Browse the curated English and Arabic voice catalog (accents, gender, standard/premium tier, preview audio). Pass the returned id as voiceId to create_agent / update_agent.

Returns: voices: array of { id, name, language, gender, accent, tier, description, previewUrl }.

ArgumentTypeRequiredDescription
language"en" | "ar"noOnly voices for this language.
gender"male" | "female"no
tier"standard" | "premium"noPremium voices cost more per minute (see get_usage for the plan's rates).
accentstringnoSubstring match on the accent label, e.g. "gulf", "egyptian", "british".

Example:

{
  "language": "ar",
  "gender": "female"
}

Phone numbers

list_number_countries — Countries where numbers are offered

Scope numbers:read · read-only.

The number catalogue: every country with the number types offered, whether each is bought instantly (search_numbers + buy_number) or on request (request_number, carrier registration handled by our team), the monthly price for this organization, inbound-only types and the documents a request needs.

Returns: countries: array of { code, name, dialCode, offers: [{ type, mode, monthlyPriceCents, inboundOnly, documents }] }, plus comingSoon.

ArgumentTypeRequiredDescription
countrystringnoOne country only (ISO 3166-1 alpha-2), e.g. AE.
mode"all" | "instant" | "request"default "all"instant = buy self-serve now; request = needs carrier registration (request_number).

Example:

{
  "mode": "instant"
}

list_numbers — List phone numbers

Scope numbers:read · read-only.

The organization's phone numbers with status, assigned agent and whether they can dial out. Use an active number with inboundOnly: false as phoneNumberId for start_call.

Returns: numbers: array of { id, phone, country, type, status, agentId, inboundOnly, monthlyPriceCents, createdAt }, plus requests (open number requests).

No arguments.

Example:

{}

get_number — Get a phone number

Scope numbers:read · read-only.

One number with its status, the agent answering it, pricing and the last provisioning error if any.

Returns: The number object.

ArgumentTypeRequiredDescription
phoneNumberIdstringyesPhone number id.

Example:

{
  "phoneNumberId": "68ee11aa00c1d2e3f405"
}

search_numbers — Search available numbers

Scope numbers:read · read-only, card.

Search the carrier inventory for numbers you could buy now, with the monthly price for this organization. Countries that need registration return no inventory and point to request_number instead. Nothing is purchased.

Returns: numbers: candidates { phone, locality, region }, plus monthlyPriceCents, includedInPlan, inboundOnly, regulatory (none | documents) and eligibility.

ArgumentTypeRequiredDescription
countrystringyesISO 3166-1 alpha-2 country code; list_number_countries shows what is offered.
type"local" | "tollfree" | "mobile"default "local"local, tollfree or mobile. Not every type exists in every country; the result says which.
containsstringnoDigits the number should contain, e.g. "555" or a vanity pattern.
areaCodestringnoArea code (US and CA only).
limitintegerdefault 10

Example:

{
  "country": "US",
  "type": "local",
  "areaCode": "415"
}

buy_number — Buy a phone number

Scope numbers:write · costs money, card.

Buy a number from search_numbers for this organization (monthly charge on the subscription unless the plan includes it). Requires confirm: true. Optionally assigns a published agent at once. Countries that need registration are not bought here: use request_number.

Returns: { purchased: true, number } when the number is active, or { purchased: false, reason, requirements?, dashboardUrl? }.

ArgumentTypeRequiredDescription
phonestringyesA number returned by search_numbers, in E.164.
countrystringyesCountry code used in the search.
type"local" | "tollfree" | "mobile"default "local"
agentIdstringnoAssign this published agent to answer the number right after purchase.
confirmbooleandefault falseMust be true. Buying a number starts a monthly charge. Ask the user before setting this.

Example:

{
  "phone": "+14155550123",
  "country": "US",
  "type": "local",
  "confirm": true
}

request_number — Request a number that needs registration

Scope numbers:write · costs money.

For countries whose offer is request (United Kingdom, UAE, Saudi Arabia, Germany, …): files a number request with the business details; our team completes the carrier registration and activates the number (usually within three business days). Supporting documents are uploaded by the user in the dashboard (the result links to the form). Requires confirm: true.

Returns: { requestId, status: "requested", dashboardUrl, note }.

ArgumentTypeRequiredDescription
countrystringyesA country whose offer is request in list_number_countries (e.g. GB, AE, SA, DE).
type"local" | "tollfree" | "mobile"default "local"
quantityintegerdefault 1
fieldsobjectyesBusiness details the carrier registration needs. Documents (licence, ID, proof of address) are uploaded later in the dashboard; the result links to the form.
confirmbooleandefault falseMust be true. A request starts a registration our team completes and ends with a monthly charge once the number is activated. Ask the user before setting this.

Example:

{
  "country": "GB",
  "type": "local",
  "fields": {
    "businessName": "Northwind Ltd",
    "address": {
      "street": "1 High Street",
      "city": "Reading",
      "postalCode": "RG1 1AA",
      "country": "GB"
    },
    "contactName": "Sara Ahmed",
    "contactEmail": "sara@northwind.example",
    "intendedUse": "Customer support line"
  },
  "confirm": true
}

assign_number — Assign a number to an agent

Scope numbers:write.

Set (or clear with null) the agent that answers inbound calls on a number. The agent must be published to actually answer.

Returns: The updated number.

ArgumentTypeRequiredDescription
phoneNumberIdstringyesPhone number id.
agentIdstring | nullyesAgent that answers inbound calls on this number, or null to unassign.

Example:

{
  "phoneNumberId": "68ee11aa00c1d2e3f405",
  "agentId": "6ad0bf49171339b53c8e"
}

release_number — Release a phone number

Scope numbers:write · irreversible.

Give a number back to the carrier and stop its monthly charge. Irreversible: callers get a disconnected tone and the number cannot be bought again for 30 days. Requires confirm: true.

Returns: { released: true, phone }.

ArgumentTypeRequiredDescription
phoneNumberIdstringyesPhone number id.
confirmbooleandefault falseMust be true. Releasing a number is irreversible: callers can no longer reach it and the number cannot be bought back for 30 days. Ask the user before setting this.

Example:

{
  "phoneNumberId": "68ee11aa00c1d2e3f405",
  "confirm": true
}

Calls (inbound and outbound)

start_call — Start an outbound call

Scope calls:write · costs money, card.

Dial a phone number with a published agent from one of the organization's numbers. note tells the agent what this call is about. Returns immediately with the call id while the phone rings; the conversation, summary and outcome arrive asynchronously (poll get_call, or receive the call.summary.ready webhook). Requires confirm: true.

Returns: { callId, status: "queued", note }. Errors: agent_not_published, number_not_active, number_inbound_only, payment_required, concurrency_limit, contact_do_not_call, country_not_allowed, call_start_failed.

ArgumentTypeRequiredDescription
agentIdstringyesA published agent (publishedVersion > 0).
tostringyesNumber to dial.
phoneNumberIdstringnoYour caller-id number. Optional when the organization has exactly one active outbound number.
contactNamestringnoName passed to the agent when the number is not a known contact.
notestringnoContext for this call only, exposed to the agent as {{call_note}}, e.g. "Confirm Thursday 10:00; offer Friday if not possible".
variablesobjectnoExtra prompt variables for this call, available as {{campaign.<key>}}.
idempotencyKeystringnoUnique per intended call (e.g. an order id). Repeating a call with the same key returns the first call instead of dialing again.
confirmbooleandefault falseMust be true. A call costs money and rings a real phone. Ask the user before setting this.

Example:

{
  "agentId": "6ad0bf49171339b53c8e",
  "to": "+14155550123",
  "contactName": "Sara Ahmed",
  "note": "Confirm Thursday's 10:00 appointment",
  "idempotencyKey": "order-8841-confirm",
  "confirm": true
}

end_call — End a live call

Scope calls:write · card.

Hang up a call that is queued, ringing or in progress (for example one started with start_call that should not continue). Requires confirm: true. Idempotent: a call that already ended is reported as such and nothing changes. The transcript, summary and cost of the ended part still arrive asynchronously (get_call).

Returns: { callId, status, alreadyEnded, endedReason, note }. Errors: not_found (unknown id or another organization's call), call_not_ready (still being set up; retry in a few seconds), call_end_failed.

ArgumentTypeRequiredDescription
callIdstringyesA call that is queued, ringing or in progress (from start_call or list_calls).
confirmbooleandefault falseMust be true. Ending a call hangs up on a real person. Ask the user before setting this.

Example:

{
  "callId": "68ef3b2c001a9d4e7f10",
  "confirm": true
}

get_call — Get a call

Scope calls:read · read-only, card.

Status and results of a call: direction, numbers, timing, cost, summary, outcome, sentiment, structured data and the actions the agent took (plain-language labels). While the call is live this is the status to poll. The transcript is a separate tool (get_transcript).

Returns: The call object; summary/outcome are null until post-call analysis finishes (usually within a minute of hang-up). endedReason is a plain-language label.

ArgumentTypeRequiredDescription
callIdstringyesCall id.

Example:

{
  "callId": "68ef3b2c001a9d4e7f10"
}

get_transcript — Get a transcript

Scope calls:read · read-only.

The conversation of a call, as text or as structured turns. Grows while the call is in progress (live transcript).

Returns: { callId, status, turns } (json) or { callId, status, text }.

ArgumentTypeRequiredDescription
callIdstringyesCall id.
format"text" | "json"default "text"text = one line per turn ("agent: …"), json = array of { role, text, at }.

Example:

{
  "callId": "68ef3b2c001a9d4e7f10",
  "format": "text"
}

Scope calls:read · read-only.

A signed, short-lived (10 minutes) URL to the call's audio recording. Fetch it again when it expires. Null when the call has no recording (in progress, failed, or deleted by retention).

Returns: { callId, url, expiresAt } or { callId, url: null, reason }.

ArgumentTypeRequiredDescription
callIdstringyesCall id.

Example:

{
  "callId": "68ef3b2c001a9d4e7f10"
}

list_calls — List calls

Scope calls:read · read-only.

Calls newest first, with optional filters by direction (inbound / outbound), agent, status, time window, contact or campaign. Paginate with cursor. Each row carries summary, outcome and sentiment once analysed.

Returns: { calls: Call[], nextCursor }.

ArgumentTypeRequiredDescription
direction"inbound" | "outbound" | "web"noInbound = callers dialled your number; outbound = start_call or campaigns; web = browser test calls.
agentIdstringnoOnly calls handled by this agent.
status"queued" | "ringing" | "in_progress" | "ended" | "failed" | "no_answer" | "busy" | "voicemail"no
sincestringnoStarted at or after this instant.
untilstringnoStarted at or before this instant.
contactstringnoA contact id or an E.164 phone number.
campaignIdstringnoOnly calls placed by this campaign.
limitintegerdefault 25Page size, 1–100.
cursorstringnoOpaque nextCursor from the previous page. Omit for the first page.

Example:

{
  "direction": "inbound",
  "status": "ended",
  "since": "2026-10-01T00:00:00Z",
  "limit": 20
}

list_live_calls — List live calls

Scope calls:read · read-only.

Calls that are queued, ringing or in progress right now, with how long each has been running and the line usage against the concurrency limit.

Returns: { calls: [{ id, direction, from, to, status, agentId, startedAt, elapsedSec }], inProgress, concurrencyLimit }.

No arguments.

Example:

{}

Outbound campaigns

list_campaigns — List campaigns

Scope campaigns:read · read-only.

Outbound campaigns with status and progress counters (queued, calling, done, failed, skipped).

Returns: campaigns: array of { id, name, status, agentId, phoneNumberId, total, queued, calling, done, failed, skipped, startAt, startedAt, completedAt }.

ArgumentTypeRequiredDescription
status"all" | "draft" | "scheduled" | "running" | "paused" | "completed" | "cancelled"default "all"

Example:

{
  "status": "running"
}

get_campaign — Get a campaign

Scope campaigns:read · read-only, card.

One campaign with its settings (window, pacing, retries) and live progress. Poll it while running.

Returns: The campaign object plus progressPct and outcomes (reached / no answer / busy / voicemail / failed counts).

ArgumentTypeRequiredDescription
campaignIdstringyesCampaign id.

Example:

{
  "campaignId": "6ad129fa2c626a8bfa43"
}

get_campaign_results — Get campaign results

Scope campaigns:read · read-only.

Per-contact rows of a campaign: state, attempts, last call id and outcome, in file order. Paginate with cursor; use get_call / get_transcript for details of a row's call.

Returns: { rows: [{ id, phone, name, status, reason, attempts, lastCallId, lastCallStatus, outcome, nextAttemptAt }], nextCursor }.

ArgumentTypeRequiredDescription
campaignIdstringyesCampaign id.
status"queued" | "calling" | "done" | "failed" | "skipped"noOnly rows in this state.
limitintegerdefault 25Page size, 1–100.
cursorstringnoOpaque nextCursor from the previous page. Omit for the first page.

Example:

{
  "campaignId": "6ad129fa2c626a8bfa43",
  "status": "done",
  "limit": 50
}

create_campaign — Create a campaign

Scope campaigns:write · costs money, card.

Create an outbound campaign from a contact list or CSV text. Rows with invalid phones, duplicates, do-not-call contacts or countries the organization may not dial are rejected and reported. start: "draft" only saves; now / at dial real phones and need confirm: true and the organization's outbound compliance acknowledgement (done once in the dashboard).

Returns: { campaignId, status, accepted, rejected: [{ line, phone, reason }], note }.

ArgumentTypeRequiredDescription
namestringyes
agentIdstringyesA published agent.
phoneNumberIdstringyesCaller-id number (active, outbound-capable).
contactsarray of objectnoThe call list. Use this or csv.
csvstringnoRaw CSV text with a header row (phone or number column required; name and any other columns become variables).
start"draft" | "now" | "at"default "draft"draft saves without dialling; now starts immediately; at schedules startAt.
startAtstringnoRequired when start = at.
timezonestringnoOrganization time zone for startAt (default: the organization's).
windowobjectnoCalling hours, applied in each destination's local time (default: the organization's outbound window).
maxConcurrentintegerdefault 2Lines this campaign may use at once (one line is always kept free for inbound).
maxAttemptsintegerdefault 2Attempts per contact when nobody answers or the line is busy.
retryDelayMinintegerdefault 120Minutes between attempts.
voicemailDetectionbooleandefault true
confirmbooleandefault falseRequired (true) when start is now or at: a campaign dials real phones and spends money. Drafts need no confirmation.

Example:

{
  "name": "October renewals",
  "agentId": "6ad0bf49171339b53c8e",
  "phoneNumberId": "68ee11aa00c1d2e3f405",
  "contacts": [
    {
      "phone": "+14155550123",
      "name": "Sara Ahmed",
      "variables": {
        "plan": "Pro"
      }
    }
  ],
  "start": "draft"
}

set_campaign_status — Start, pause, resume or cancel a campaign

Scope campaigns:write · costs money, card.

Move a campaign: draft/paused → running (dials phones, needs confirm), running → paused, any open state → cancelled (irreversible, needs confirm).

Returns: The campaign with its new status.

ArgumentTypeRequiredDescription
campaignIdstringyesCampaign id.
status"running" | "paused" | "cancelled"yesrunning = start or resume; paused = stop dialling, keep the queue; cancelled = stop for good.
confirmbooleandefault falseRequired (true) for running (dials phones, spends money) and cancelled (irreversible). Pausing needs no confirmation.

Example:

{
  "campaignId": "6ad129fa2c626a8bfa43",
  "status": "running",
  "confirm": true
}

Contacts

list_contacts — List contacts

Scope contacts:read · read-only.

Contacts newest first, filtered by free-text search (name, phone prefix, email), exact phone, tag or do-not-call flag. Paginate with cursor.

Returns: { contacts: Contact[], nextCursor }.

ArgumentTypeRequiredDescription
searchstringnoName, phone prefix or email.
phonestringnoExact match (at most one result).
tagstringnoContacts carrying this tag.
doNotCallbooleanno
limitintegerdefault 25Page size, 1–100.
cursorstringnoOpaque nextCursor from the previous page. Omit for the first page.

Example:

{
  "search": "sara",
  "limit": 20
}

get_contact — Get a contact

Scope contacts:read · read-only.

One contact by id or phone, including the memory summary the agent is given at the start of each call, attributes, tags, notes and call count.

Returns: The contact, or an error not_found.

ArgumentTypeRequiredDescription
contactIdstringnoContact id.
phonestringnoPhone number in E.164 format, e.g. +14155550123.

Example:

{
  "phone": "+14155550123"
}

upsert_contact — Create or update a contact

Scope contacts:write.

Create the contact for a phone number or update it when it exists. Attributes merge; tags replace.

Returns: { created: boolean, contact }.

ArgumentTypeRequiredDescription
phonestringyesIdentifies the contact: created when unknown, updated otherwise.
namestring | nullno
emailstring | nullno
attributesobjectnoKey/value strings merged into the stored attributes (set a key to "" to clear it). The agent can read and update them during calls.
tagsarray of stringnoReplaces the tag list.
notesstringnoInternal notes (not spoken by the agent).
doNotCallbooleanno

Example:

{
  "phone": "+14155550123",
  "name": "Sara Ahmed",
  "tags": [
    "vip"
  ],
  "attributes": {
    "plan": "pro"
  }
}

set_do_not_call — Set do-not-call

Scope contacts:write.

Block (or unblock) outbound calls to a contact. start_call and campaigns refuse blocked contacts.

Returns: The contact with the new flag.

ArgumentTypeRequiredDescription
contactIdstringnoContact id.
phonestringnoCreates the contact when it does not exist yet, so the flag sticks.
doNotCallbooleandefault truetrue blocks outbound calls to this contact; false lifts the block.

Example:

{
  "phone": "+14155550123",
  "doNotCall": true
}

Balance and billing

get_usage — Balance, plan and usage

Scope usage:read · read-only, card.

Plan, included minutes left this period, prepaid wallet balance, number allowance, concurrency limit, per-minute overage rates and what this period cost so far. Check it before placing many calls or buying numbers.

Returns: { organization, plan, period, minutes, wallet, numbers, concurrency, rates, periodSpend }.

No arguments.

Example:

{}

list_invoices — List invoices

Scope billing:read · read-only.

Recent invoices of the subscription (amount, status, period, hosted PDF link).

Returns: invoices: array of { id, number, status, amountCents, currency, createdAt, periodStart, periodEnd, url }.

ArgumentTypeRequiredDescription
limitintegerdefault 12

Example:

{
  "limit": 6
}

Scope billing:read · read-only, card.

A checkout link that adds a prepaid pack to the wallet. Nothing is charged by this tool: the user pays on the hosted checkout page. Requires an active subscription.

Returns: { url, amountCents, expiresNote }.

ArgumentTypeRequiredDescription
amountCentsintegerdefault 5000Prepaid pack in cents: 2500, 5000, 10000 or 25000 ($25, $50, $100, $250).

Example:

{
  "amountCents": 5000
}

Scope billing:read · read-only, card.

A link to pick a plan (trial organizations: checkout for planId) or to change / cancel the plan and update the card (subscribed organizations: the billing portal). Nothing is charged by this tool.

Returns: { url, kind: "checkout" | "portal" }.

ArgumentTypeRequiredDescription
planId"starter" | "growth" | "scale"noPlan to subscribe to (trial organizations). Subscribed organizations get a billing-portal link to change plans instead; omit planId for the portal.

Example:

{
  "planId": "growth"
}

Integrations

list_integrations — List integrations

Scope integrations:read · read-only.

The verified tool catalogue (calendars, CRMs, helpdesks, spreadsheets, messaging) with each tool's connection status for this organization and the action slugs an agent can use during calls (pass them to update_agent tools).

Returns: integrations: array of { toolkit, name, category, status, connectedBy, actions: [{ slug, title, midCall }] }, plus connectedCount.

ArgumentTypeRequiredDescription
connectedOnlybooleandefault falseOnly tools the organization has connected.

Example:

{
  "connectedOnly": true
}

Scope integrations:read · read-only.

A link the user opens (signed in to the dashboard) to connect a tool with their own account; the sign-in happens on the tool's own site.

Returns: { url, toolkit, note }.

ArgumentTypeRequiredDescription
toolkitstringyesToolkit slug from list_integrations, e.g. googlecalendar, hubspot, slack.

Example:

{
  "toolkit": "googlecalendar"
}

Webhooks

list_webhooks — List webhooks

Scope webhooks:read · read-only.

Registered webhook endpoints with their events, active flag and the last deliveries (status, attempts).

Returns: webhooks: array of { id, url, events, active, recentDeliveries }.

No arguments.

Example:

{}

create_webhook — Create a webhook

Scope webhooks:write.

Register an HTTPS endpoint that receives signed events (call.started, call.ended, call.summary.ready, number.activated, wallet.low). The signing secret is returned once; store it to verify the X-AgentCalls-Signature header.

Returns: { id, url, events, secret } — the secret is never shown again.

ArgumentTypeRequiredDescription
urlstringyesHTTPS endpoint that receives signed JSON events.
eventsarray of "call.started" | "call.ended" | "call.summary.ready" | "number.activated" | "wallet.low"yesEvents to deliver.

Example:

{
  "url": "https://example.com/agentcalls",
  "events": [
    "call.summary.ready"
  ]
}

test_webhook — Send a test event

Scope webhooks:write.

POST a signed sample call.ended event to the endpoint and report the HTTP status and latency. Nothing is queued or retried.

Returns: { ok, status, durationMs, error? }.

ArgumentTypeRequiredDescription
webhookIdstringyesWebhook id.

Example:

{
  "webhookId": "68ef3c10000a1b2c3d4e"
}

Organization

get_organization — Get organization settings

Scope org:read · read-only.

Name, language, status, plan, home country, concurrency limit and the outbound calling rules (allowed countries, calling hours, time zone, consent text, compliance acknowledgement).

Returns: { id, name, locale, status, planId, homeCountry, concurrencyLimit, outbound: { allowedCountries, defaultCountries, window, timezone, consentText, acknowledged } }.

No arguments.

Example:

{}

update_outbound_settings — Update outbound calling rules

Scope org:write.

Change which countries outbound calls may dial, the default calling hours and time zone, and the outbound consent text. The compliance acknowledgement (TCPA / do-not-call responsibility) cannot be given here: a person must tick it in the dashboard before campaigns can start.

Returns: The outbound settings after the change.

ArgumentTypeRequiredDescription
allowedCountriesarray of stringnoISO 3166-1 alpha-2 codes outbound calls may dial. Empty list = the default (the organization's own country plus the countries of its numbers).
windowobjectnoDefault calling hours for campaigns, in each destination's local time.
timezonestringnoIANA time zone of the organization.
consentTextstringnoOutbound consent / recording text agents read at the start of outbound calls (empty = the agent's own).

Example:

{
  "allowedCountries": [
    "AE",
    "SA",
    "US"
  ],
  "window": {
    "start": "09:00",
    "end": "20:00",
    "days": [
      0,
      1,
      2,
      3,
      4
    ]
  },
  "timezone": "Asia/Dubai"
}

Resources

  • agentcalls://skill — AgentCalls skill (SKILL.md): How to run a phone line through this server end to end: setup → agent → number → test call → inbound/outbound → summaries and webhooks → billing. Read it before a long task; it follows the Agent Skills format (agentskills.io).
  • agentcalls://skill/{file} — Skill reference file: A reference file of the skill: workflows.md, tools.md (generated from this server's tool list), safety.md, errors.md.
  • agentcalls://templates — Agent templates: The four agent templates (receptionist, appointment booking, lead qualification, support) in English and Arabic: instructions, opening line, consent text, analysis schema and built-in tools. Read one with agentcalls://templates/{template}/{language}.
  • agentcalls://templates/{template}/{language} — One agent template: Full text of a template, e.g. agentcalls://templates/receptionist/ar.
  • agentcalls://voices — Voice catalog: Every voice with language, gender, accent, tier and preview URL (same data as list_voices).
  • agentcalls://voices/{language} — Voices for a language: Voices for en or ar.
  • agentcalls://countries — Number catalogue: Countries, number types, instant-vs-request mode and documents (same data as list_number_countries, without prices).

Prompts

  • use_agentcalls_skill — Load the AgentCalls skill: Inserts the skill (SKILL.md): the end-to-end workflow, the rules about money and confirmations, and where to look things up.
  • set_up_phone_line — Set up a phone line from scratch: Guided setup: check the account, design and publish an agent, get a number (buy or request), test it, and hand over.
  • design_receptionist_agent — Design a receptionist agent: Walks through choosing a voice, writing instructions, creating, publishing and assigning a number for a phone receptionist.
  • review_recent_calls — Review recent calls: Pulls recent calls and summarizes outcomes, sentiment, failures and follow-ups.
  • place_follow_up_call — Place a follow-up call: Checks the contact and do-not-call status, confirms with the user, then starts the call and reports the result.
  • run_campaign — Run an outbound campaign: Collects the list, checks compliance and balance, creates the campaign as a draft, reviews rejected rows, then starts it with the user's go-ahead and reports progress.

agentcalls/references/safety.md

Safety, money and compliance

Money

  • Calls cost per minute: included minutes first, then the prepaid wallet at rates.overageStandardCentsPerMinute or overagePremiumCentsPerMinute (premium voices). Numbers cost monthlyPriceCents per month unless includedInPlan. get_usage is the source of truth; quote it before expensive actions (campaigns, many calls, several numbers).
  • Low balance: when wallet.lowBalance is true, say so and offer get_topup_link; calls stop when minutes and wallet are exhausted (payment_required).
  • Never buy, request or release numbers, start calls or campaigns, or hang up without the user's explicit yes in this conversation; then and only then set confirm: true. A yes for one call is not a yes for the next.

Phone etiquette and law

  • The user is responsible for consent and recording law in each country; agents play a recording announcement by default (consentAnnouncement). Do not switch it off unless the user asks and understands the implication.
  • Outbound calls respect the organization's allowed countries and calling hours (campaigns) and the do-not-call flag on contacts. Do not try to work around a contact_do_not_call, country_not_allowed or not_acknowledged error; explain it and offer the legitimate fix (ask the user to allow the country, or to tick the compliance acknowledgement in the dashboard).
  • Do not place calls to emergency numbers, premium-rate numbers or numbers the user has no relationship with. Decline requests that look like harassment, impersonation of authorities, or scams.
  • Test calls: prefer the free in-browser test (test_agent); a phone test to the user's own number is fine after their yes.

Data

  • Transcripts, summaries and recordings are the organization's customer data. Quote only what the user needs; do not forward recordings or transcripts to third parties unless the user asks.
  • Recording links expire after 10 minutes; fetch again rather than storing them.
  • Webhook secrets and API keys are shown once; tell the user to store them, never repeat them later.

When things fail

  • internal or provider errors: retry once after a short wait, then tell the user with the time of the attempt.
  • A tool call never waits for a phone call; if a call seems stuck in queued/ringing for more than two minutes, report it and offer end_call.

agentcalls/references/errors.md

Error codes

Tool failures come back as isError: true with { error: { code, message, details? } }. Read message; it usually names the fix.

CodeMeaningWhat to do
confirmation_requiredA money, hang-up or irreversible tool was called without confirm: true.Ask the user; call again with confirm: true only after a yes.
forbiddenThe connection or key lacks the scope.OAuth: the client re-asks the user for the permission (step-up); API key: add the scope under Settings → Developers.
invalid_requestArguments are wrong or incomplete (details lists them).Fix the arguments.
not_foundUnknown id, or another organization's object.Use ids from list tools.
validation_failedThe agent draft has blocking issues (details).update_agent the listed fields.
validation_unavailableThe voice platform could not be reached for validation.Retry in a minute.
agent_not_publishedThe agent has no published version.publish_agent.
number_not_active / number_inbound_onlyThe caller-id number cannot dial out.list_numbers; pick an active outbound number or buy one.
payment_requiredSuspended, cancelled, or no minutes and no wallet.get_usage; offer get_topup_link / get_plan_link.
concurrency_limitEvery line is busy.Wait for a call to end (list_live_calls).
contact_do_not_callThe contact is blocked.Do not call; tell the user.
country_not_allowedOutbound to that country is not enabled.update_outbound_settings after the user's yes, or the dashboard.
not_acknowledgedCampaigns need the compliance acknowledgement.A person ticks it in the dashboard (Settings → Outbound calling).
call_start_failed / call_end_failedThe voice platform refused.Retry once; then report.
call_not_readyThe call is still being set up.Retry end_call in a few seconds.
not_eligibleNumbers need a paid plan / the subscription is past due.get_plan_link.
invalid_cursorA pagination cursor is stale.Start again without cursor.
rate_limited (HTTP 429)60 requests per minute.Wait Retry-After seconds.
internalOur side failed.Retry; then report with the time.

HTTP-level: 401 means the token expired or the key is invalid (the client re-authenticates); 403 with insufficient_scope means re-consent with more permissions.