mcpField
S
setup-inbound

setup-inbound

github.com/bolna-aiAgent workflows368 installsUpdated today
Repository

About setup-inbound

A Bolna skill for linking phone numbers to agents that answer inbound calls. It also covers optional IVR routing, caller identification, spam controls, and multilingual message switching.

Key features

  1. 01Map or unlink phone numbers
  2. 02Configure Plivo IVR menus and input collection
  3. 03Populate prompts with caller data
  4. 04Set inbound limits and caller rules
  5. 05Configure multilingual message switching

Use cases

  1. 01Route an inbound number to an agent
  2. 02Collect account details before routing a call
  3. 03Personalize calls using caller records
  4. 04Limit calls from unknown or frequent callers

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/bolna-ai/skills --skill setup-inbound

After installing, type/setup-inboundin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

assets/
ivr_account_verification.json
ivr_department_routing.json
ivr_multilingual.json
references/
auto-switch-messages.md
caller-identification.md
ivr-config.md
scripts/
setup_inbound.py

Details

From the project README

Bolna Inbound Calls

Inbound = map a phone number to an agent so calls to that number are answered automatically. Optionally layer IVR menus, caller identification, spam controls, and multilingual switching on top.

Two endpoints — that's it

EndpointPurpose
POST /inbound/setupMap a phone number to an agent (with optional IVR config)
POST /inbound/unlinkUnmap

Both: Authorization: Bearer $BOLNA_API_KEY, Content-Type: application/json.

Where phone_number_id comes from

Number sourceHow to get the ID
Bolna-hosted (purchased on dashboard)GET /phone-numbers/all — see manage-phone-numbers
Connected provider (your Twilio, Plivo, Vobiz, Exotel)Returned when you list phone numbers after connecting the provider
Your own SIP trunk (BYOT)Returned from POST /sip-trunks/trunks/{id}/numbers — see setup-sip-trunk

Whatever the source, the mapping flow is identical.

Basic mapping

curl -X POST https://api.bolna.ai/inbound/setup \
  -H "Authorization: Bearer $BOLNA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
    "phone_number_id": "123e4567-e89b-12d3-a456-426614174000"
  }'

Response:

{
  "url": "https://api.bolna.ai/inbound_call?agent_id=...&user_id=...",
  "phone_number": "+19876543210",
  "id": "3c90c3cc0d444b5088888dd25736052a"
}

That's the whole basic setup. Now any call to +19876543210 is answered by the agent.

One number, one agent. Re-mapping a number to a different agent silently unmaps the previous link. To detach without re-mapping, use /inbound/unlink.

IVR (menu routing)

For "press 1 for sales, 2 for support" or pre-call data collection. Plivo only.

curl -X POST https://api.bolna.ai/inbound/setup \
  -H "Authorization: Bearer $BOLNA_API_KEY" \
  -H "Content-Type: application/json" \
  -d @assets/ivr_department_routing.json

The asset file:

{
  "agent_id": "default-agent-uuid",
  "phone_number_id": "phone-number-uuid",
  "ivr_config": {
    "enabled": true,
    "voice": "Polly.Aditi",
    "welcome_message": "Welcome to Acme Corp.",
    "timeout": 5,
    "max_retries": 2,
    "default_agent_id": "default-agent-uuid",
    "steps": [
      {
        "step_id": "department",
        "type": "menu",
        "prompt": "Press 1 for Sales. Press 2 for Support. Press 3 for Billing.",
        "field_name": "department",
        "options": [
          { "digit": "1", "label": "Sales",   "agent_id": "sales-agent-uuid" },
          { "digit": "2", "label": "Support", "agent_id": "support-agent-uuid" },
          { "digit": "3", "label": "Billing", "agent_id": "billing-agent-uuid" }
        ]
      }
    ]
  }
}
Step types
TypePurpose
menuPresent options, route based on digit pressed.
collectCapture a multi-digit input (account number, PIN).
Top-level IVR fields
FieldDefaultNotes
enabledfalseMaster toggle.
voicePolly.JoannaPolly voice for the IVR prompts. Polly.Aditi for Hindi-English.
welcome_message—Played once on connect, before the first step.
timeout5Seconds to wait for caller input on a step.
max_retries2Retries on invalid input per step.
invalid_input_message"Invalid input. Please try again."Played on wrong key.
no_input_message"No input received. Goodbye."Played on timeout.
steps[]requiredIVR flow.
default_agent_id—Fallback agent when no option-level agent_id is set.
Step navigation
FieldBehaviour
next_stepLinear: go to this step after current.
conditional_nextBranch by digit: {"1": "menu_en", "2": "menu_hi"}.
NeitherEnd of IVR. Route to selected agent_id or default_agent_id.
Multi-language IVR with branching
{
  "step_id": "language",
  "type": "menu",
  "prompt": "For English press 1. Hindi ke liye 2 dabayein.",
  "field_name": "language",
  "options": [
    { "digit": "1", "label": "English" },
    { "digit": "2", "label": "Hindi" }
  ],
  "conditional_next": { "1": "menu_en", "2": "menu_hi" }
}

See assets/ivr_multilingual.json for the full multilingual department-routing flow.

What the agent sees

After IVR completes, the collected fields are available as recipient_data:

{
  "department": "Sales",
  "account_number": "123456",
  "language": "English",
  "ivr_completed_at": "2026-05-19T10:30:00Z"
}

Use them in agent prompts: "Customer selected {department}. Account number: {account_number}."

Disable IVR
curl -X POST https://api.bolna.ai/inbound/setup \
  ... \
  -d '{ "agent_id": "...", "phone_number_id": "...", "ivr_config": { "enabled": false } }'

For full IVR reference (menu + collect step examples, conditional branching), see references/ivr-config.md.

Caller identification (ingest_source_config)

Pre-fill the agent's prompt variables based on who's calling. Configured on the agent, not on /inbound/setup. Three flavours:

SourceBest for
Internal APITeams with an existing CRM / customer database. Real-time, fully dynamic.
CSV uploadSmall teams, low-volume, no-code. Static — re-upload to update.
Google SheetReal-time sync without coding. Sheet must be publicly accessible.
Internal API source

Bolna does a GET request to your endpoint with:

GET https://your-api.example.com/customers
    ?contact_number=+19876543210
    &agent_id=<agent_uuid>
    &execution_id=<execution_uuid>

Auth: Bearer token (configured per agent).

Your response JSON is merged into recipient_data. Use it in prompts:

You are speaking with {first_name} {last_name}. They are on the {plan} plan,
last contacted on {last_contacted_at}.
CSV / Google Sheet source

Both need a contact_number column (with country code). All other columns become prompt variables.

contact_number,first_name,last_name,plan,last_contacted_at
+19876543210,Priya,Sharma,Enterprise,2026-05-12
+918765432109,Rahul,Verma,Pro,2026-05-14

For full setup on each source, see references/caller-identification.md.

Spam and abuse controls

These live on the agent's task_config, not on the inbound mapping itself.

FieldNotes
inbound_limitMax calls per recipient phone number. -1 = unlimited. Useful to prevent a single number from running up costs.
whitelist_phone_numbers[]E.164 numbers that always bypass any limits / unknown-caller rules.
disallow_unknown_numbersBoolean. When true, reject callers not present in the agent's ingest_source_config lookup. Requires ingest_source_config to be set.
{
  "task_config": {
    "inbound_limit": 3,
    "whitelist_phone_numbers": ["+919876543210", "+19876543210"],
    "disallow_unknown_numbers": true
  }
}

If disallow_unknown_numbers: true and ingest_source_config is not configured, every inbound call will be rejected. Always configure caller identification first.

Multilingual auto-switching

Bolna can auto-detect the caller's language after ~3 conversation turns and switch system messages (hangup, user-online check, tool wait) to that language. Configure language variants in the agent:

{
  "check_user_online_message": {
    "en": "Hey, are you still there?",
    "hi": "क्या आप अभी भी वहाँ हैं?",
    "ta": "வணக்கம், நீங்கள் இன்னும் இணைப்பில் இருக்கிறீர்களா?"
  },
  "call_hangup_message": {
    "en": "Thank you for calling. Goodbye!",
    "hi": "कॉल करने के लिए धन्यवाद। अलविदा!"
  }
}

Fallback order: detected language → en → first available. Always configure en as a safety net. See references/auto-switch-messages.md.

Unlink

curl -X POST https://api.bolna.ai/inbound/unlink \
  -H "Authorization: Bearer $BOLNA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phone_number_id": "123e4567-e89b-12d3-a456-426614174000" }'

The number remains owned (in your provider / SIP trunk / Bolna inventory). Only the agent link is removed.

Going deeper

FileContents
references/ivr-config.mdAll step types, navigation, voices, advanced patterns.
references/caller-identification.mdAPI, CSV, Google Sheet — setup walkthroughs and prompt-variable wiring.
references/auto-switch-messages.mdMultilingual message bundles, fallback order, supported codes.
assets/ivr_department_routing.jsonPlain department-routing IVR.
assets/ivr_multilingual.jsonLanguage selection → department menu, with separate agents per language.
assets/ivr_account_verification.jsonTwo-step collect flow (account + PIN) before agent.
scripts/setup_inbound.pyWraps POST /inbound/setup with flags for plain and IVR setups.

Troubleshooting

SymptomCauseFix
Call rings but isn't answeredphone_number_id not yet mappedRe-run POST /inbound/setup; verify with GET /phone-numbers/all
IVR returns 400Using IVR on non-Plivo numberIVR is Plivo-only; route via Plivo or skip IVR
recipient_data empty in promptingest_source_config not set, or contact_number didn't matchVerify the source has the calling number; check Bolna logs for the lookup HTTP status
All callers rejecteddisallow_unknown_numbers: true without ingest_source_configAdd the source first, or set disallow_unknown_numbers: false
Auto-switch hangup plays in wrong languageDetection needed 3 turns, call was shorterConfigure en fallback; expect the first turn or two to still be primary language
Agent picks up but audio quality is bad on inbound SIPulaw codec mismatchBolna sets ulaw on SIP-routed inbound; verify your trunk allows ulaw

See also

  • manage-phone-numbers — buying or listing Bolna numbers (where phone_number_id comes from).
  • setup-sip-trunk — wiring BYOT numbers into Bolna.
  • create-agent — task_config (spam controls) and ingest_source_config live in the agent body.
  • setup-tools/references/dtmf.md — keypad input inside a call (different from IVR menu routing).
  • https://raw.githubusercontent.com/bolna-ai/skills/main/references/india-compliance.md — DLT / 140 / 160-series rules if you're using regulated Indian numbers.

Comments

Markdown supported