Skip to main content
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):
The key is tied to a user account in Kupe (same as dashboard login). For protected /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 against https://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).
  1. Register the line under your account (if it is not already). Use the /api/v1/phone routes in Platform REST APIs and the Incoming calls group in the OpenAPI sidebar (Try it on POST /api/v1/phone/mapping).
  2. Create the mapping with POST https://api.kupe.in/api/v1/phone/mapping:
  • Send exactly one of agent_id or workflow_id.
  • Authenticate with x-api-key or Bearer JWT. You do not need webhook_url, config_source, or user_id in the body; the server fills those from deployment defaults and the authenticated user when they are omitted.
Inspect: 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 in agent_specific_config (stored in agent_specific_configs.configuration). You can set it on:
  • POST /api/v1/agents — include agent_specific_config in 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.
Read current values from 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
Example — update behavior only
OpenAPI schema: 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_id from agent_specific_config.
  • Update (clear): "background_noise_id": null or "background_noise_id": "".
  • Verify: GET /api/v1/agents/{agent_id}configurations.agent_specific_config should not contain a non-empty background_noise_id.
  • Dashboard: choose None under Background noise asset (same as clearing via API).
  • Mute only: keep an id but set "background_noise_volume": 0.
Example workflow
Interactive docs: Noise AssetsList available background noise assets in the API reference.

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

Send Content-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}}.
Variables are stored on the call session and applied when the telephony webhook connects the media stream. When a 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:
India — Bearer session (with prompt variables):
Typical success JSON
Use 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 invalid x-api-key / Bearer
  • 403 — invalid Origin
  • 500 — 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

  1. Start call (your CRM → Kupe)POST /api/call/create_call with x-api-key, or signed POST /api/v1/webhooks/agents/{agent_id}/incoming.
  2. 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.
See Agent webhooks for curl examples, HMAC signing, and config API (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_call
  • incoming.enabled + server-managed HMAC secret
  • outgoing.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 pass RBACMiddleware:
  • 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.
Unknown 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:
Observed 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_scopes rows; 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_name on PUT /api/v1/agents/{agent_id}; legacy model_provider_id, tts_provider_id, and STT provider_id still accept the same catalog codes or row UUIDs.