For Mintlify-style interactive docs (grouped endpoints, Try it, request/response panels), open the API reference tab — start at Introduction, then use the Voice Agent API sidebar (generated from
kupe-voice-agent.openapi.yaml). Try GET /health first (no API key), then GET /api/v1/me with your x-api-key or Bearer token.Base URL and authentication
All protected routes use the production API origin:https://api.kupe.in
For server integrations, send the Kupe API key (no Bearer prefix):
/api/v1/** routes, backend resolves user context from the key/JWT automatically (for example file upload/listing endpoints).
For dashboard / browser flows you can instead send a Supabase session token:
GET /api/v1/me works with either x-api-key or Bearer and returns user_id, email, optional phone, profile_image_url (when set on the auth user or OAuth provider metadata), auth (api_key or bearer), full_name, and a sanitized user_metadata object.
Sandbox values used for this document
These credentials were used to manually verify connectivity againsthttps://api.kupe.in on 2026-04-23 (see Live verification below).
Treat API keys like passwords: this page embeds a real key for integration testing—rotate it in the Kupe dashboard after you finish QA, and prefer environment variables in shipped clients.
Field mapping (client checklist → Kupe)
Your integration checklist maps to Kupe as follows.Incoming calls (PSTN) — map a line to an assistant
When someone dials your purchased PSTN number, Kupe needs a mapping from that line to the agent (or workflow) that should run. Number format: store and send destinations in international E.164 where possible — for example +91 plus ten digits for a typical India mobile, or +1 plus ten digits for a US/Canada NANP number (URL-encode+ as %2B in query strings).
- Register the line under your account (if it is not already). Use the
/api/v1/phoneroutes in Platform REST APIs and the Incoming calls group in the OpenAPI sidebar (Try it onPOST /api/v1/phone/mapping). - Create the mapping with
POST https://api.kupe.in/api/v1/phone/mapping:
- Send exactly one of
agent_idorworkflow_id. - Authenticate with
x-api-keyor Bearer JWT. You do not needwebhook_url,config_source, oruser_idin the body; the server fills those from deployment defaults and the authenticated user when they are omitted.
GET /api/v1/phone/mapping/agent/{agent_id}, GET /api/v1/phone/mapping/{phone_number_id}.
1. Agent management
List agents (summary)
GET /api/v1/agents/summary
Query: page, page_size, optional name_search.
Get full agent
GET /api/v1/agents/{agent_id}
Returns CompleteAgentResponse: agent plus configurations (model_config, tts_config, transcriber_config, vad_config, inferencing_config, tools, agent_specific_config).
Create agent
Two create paths:
After
POST /api/v1/agents/simple, configure models, voice, and behavior with PUT /api/v1/agents/{agent_id} (partial body). VAD and inferencing stay on platform defaults unless you change them in advanced flows.
Set the welcome message under agent.first_response_message, or agent.welcome_message (alias). GET /api/v1/agents/{agent_id} returns it on agent.first_response_message.
Catalog strings: use model_name, tts_model_name, transcriber_model_name (legacy *_provider_id / STT provider_id still work). See Model providers and Supported models & providers.
Agent-specific configuration
Per-agent runtime behavior lives inagent_specific_config (stored in agent_specific_configs.configuration). You can set it on:
POST /api/v1/agents— includeagent_specific_configin the create body (omitted keys use platform defaults).PUT /api/v1/agents/{agent_id}— send only the keys to change; the API merges them onto the stored config.
GET /api/v1/agents/{agent_id} → configurations.agent_specific_config.
Set
agent.apply_noise_reduction on the same create/update body (not inside agent_specific_config) to enable inbound noise reduction before VAD/STT.
Example — create with behavior
AgentSpecificConfigInput in the API reference.
List background noise assets
GET /api/v1/noise-assets
Returns the catalog of WAV assets you can assign to an agent for outbound room tone (mixed under agent speech on the live WebSocket path). Authenticate with x-api-key or Authorization: Bearer.
Each row includes:
An empty array
[] means no noise files are deployed on the server yet.
No background voice
To send only agent speech with no ambient track:- Create: omit
background_noise_idfromagent_specific_config. - Update (clear):
"background_noise_id": nullor"background_noise_id": "". - Verify:
GET /api/v1/agents/{agent_id}→configurations.agent_specific_configshould not contain a non-emptybackground_noise_id. - Dashboard: choose None under Background noise asset (same as clearing via API).
- Mute only: keep an
idbut set"background_noise_volume": 0.
Create simple agent (defaults only)
POST /api/v1/agents/simple
Body: { "name": "...", "description": "..." } — fastest path when defaults are acceptable end-to-end. Set agent_specific_config afterward with PUT /api/v1/agents/{agent_id}.
Update agent
PUT /api/v1/agents/{agent_id}
Body: AgentUpdateRequest (partial fields). Active agents reject config changes unless force_update=true where supported. Include agent: { "first_response_message": "..." } for the welcome line only, or agent_specific_config for behavior-only updates (merged).
Delete agent
DELETE /api/v1/agents/{agent_id}
2. Provider discovery
Use these lists to populate UI or to resolve codes vs UUIDs for agent creation.
3. Outbound call initiation
POST /api/call/create_call
This route is not under /api/v1. Authenticate every request with x-api-key (best for integrations and cron jobs) or Authorization: Bearer plus your Supabase session JWT (same token the Kupe web app uses). The API key owner or Bearer user is always who is billed and whose phone_numbers / mappings are used — there is no user_id query parameter.
Query parameters
Optional JSON body — prompt variables
SendContent-Type: application/json with a variable_values object to personalize the call at dial time. Each key replaces a matching {{key}} placeholder in the agent system prompt and first-response message (same behavior as campaign/batch calls, without creating a campaign).
Example agent prompt: Hello {{name}}, I'm calling about your {{balance}} balance at {{company}}.
phone_numbers row is resolved (explicit id or via mapping), Kupe uses that row’s configured telephony backend (Twilio, Elison, or Exotel). If no row is found, a fallback may use the first available active line for that user.
Optional header Origin: if present, must pass Kupe’s allow-list (validate_origin). Omit Origin for server-to-server curl.
Examples
US/Canada — API key:
call_session_id as the primary Kupe session id; use request_id to correlate logs and GET /api/v1/call-analytics?request_id=…. telephony_leg_id is an opaque carrier leg id when present. Initiation errors may return HTTP 200 with "status": "failed" and the same ids for tracing.
Failure cases
402— billing / credits (subject user)401— missing or invalidx-api-key/ Bearer403— invalidOrigin500— server/telephony configuration error
4. Knowledge base & files
There is no single public “scrape this URL into the vector DB” REST route for voice agents; URL ingestion exists in other product flows (e.g. text builder over WebSocket). For HTTP integrations, use file upload + agent mapping.Upload file (RAG)
POST /api/v1/files/upload (multipart/form-data)
List user files
GET /api/v1/files/user/me
Get / update / delete file
GET /api/v1/files/{file_id}PUT /api/v1/files/{file_id}DELETE /api/v1/files/{file_id}
Attach / detach file to agent (mappings)
Vector search (optional)
POST /api/v1/search/semantic?agent_id={agent_id} — RAG search scoped to an agent’s documents.
5. Webhooks (outbound integration + post-call)
Outbound call loop
- Start call (your CRM → Kupe) —
POST /api/call/create_callwithx-api-key, or signedPOST /api/v1/webhooks/agents/{agent_id}/incoming. - Call ends (Kupe → your CRM) — if the agent has outgoing webhook enabled and the web/phone toggles match, Kupe POSTs the lead payload to your URL.
GET/PUT /api/v1/webhooks/agents/{agent_id}, GET /api/v1/webhooks/me).
Agent toggles live under Advance → Webhooks (agent_specific_config.webhooks):
trigger_on_web_call/trigger_on_phone_callincoming.enabled+ server-managed HMACsecretoutgoing.url/method/headers/auth/retry_count
System default lead schema
Every ended call also runs the system post-call analysis (no rule attachment required). Fields include lead info (customer_name, mobile_number, …), call details, and AI qualification (lead_status, lead_temperature, lead_score, ai_summary, next actions). Legacy keys (call_summary, user_name, …) remain as compat aliases.
Custom post-analysis rules (optional)
You can still attach dashboard Post analysis rules / HTTP tools. Those run in addition to the system default and outgoing webhook dispatch.6. RBAC, API key scopes, and feature flags
RBAC (API keys)
API keys must passRBACMiddleware:
- Paths allowed for the developer role come from
milli_ai_backend/rbac_paths.json(e.g./api/v1/agents,/api/v1/providers,/api/v1/files,/api/v1/agent-files-mappings,/api/v1/search,/api/v1/phone, …). - Rows in
api_key_scopes(allowed_path_prefix,allowed_method) are now evaluated in addition to role prefixes, so narrowly scoped keys work.
request.state.role values now fall back to developer path list so keys stay usable.
Feature flags
If the same user hits the dashboard with a Bearer token,FeatureAccessMiddleware may gate some routes by user_roles.feature_flags. API-key-only requests hit FeatureAccess before the user is attached and therefore bypass that gate for mapped paths—RBAC remains the control for keys.
Live verification (2026-04-23)
Commands run from a developer machine against production:
So at verification time, authenticated REST routes failed RBAC for this key. After deploying this repository’s updates (
rbac_middleware.py, expanded rbac_paths.json), re-run the same curls: you should receive 200 and JSON provider/agent payloads.
Changelog (this repo)
- RBAC: API keys honor
api_key_scopesrows; unknown roles fall back to developer paths; developer path includes providers, agent-files-mappings, search, phone. - Agents: Prefer
model_name,tts_model_name,transcriber_model_nameonPUT /api/v1/agents/{agent_id}; legacymodel_provider_id,tts_provider_id, and STTprovider_idstill accept the same catalog codes or row UUIDs.

