All pages

Docs

API reference

Every voicast API endpoint (/v1) and the voice connections: authentication, format, inputs, responses and errors.

View as Markdown

Basics#

  • URL: https://api.voicast.jp/v1
  • Auth: Authorization: Bearer vk_live_… or vk_test_…. A key reads and writes only inside its environment (live or test)
  • Format: JSON. Times are Unix milliseconds; IDs are prefixed strings (agt_, ten_, call_, sess_, con_)
  • Errors: { error, message, details?, request_id }. See Errors and limits
  • Request ID: every response has x-request-id (req_…), the same as request_id in error bodies. Quote it when contacting support
  • Rate limit: 600 requests per minute per API key. x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset (end of the window, Unix seconds) show what is left; over the limit you get 429 with Retry-After (seconds)

Create API keys under API keys in the dashboard (admin or above). Keep them on your server; never ship them to browsers or apps.

Endpoints#

Method Path Description
POST /v1/agents Create an agent
GET /v1/agents List agents
GET /v1/agents/{id} Get an agent
PUT /v1/agents/{id} Update an agent (new version)
GET /v1/agents/{id}/versions List versions
DELETE /v1/agents/{id} Delete an agent
POST /v1/tenants Create a tenant
GET /v1/tenants List tenants
GET /v1/tenants/{id} Get a tenant
PATCH /v1/tenants/{id} Update a tenant
POST /v1/tenants/{id}/secret Rotate the connection secret
DELETE /v1/tenants/{id} Delete a tenant
GET /v1/calls List calls
GET /v1/calls/{id} Get a call
GET /v1/calls/by-provider/{providerCallId} Get a call by your provider's ID
GET /v1/calls/{id}/transcript Get a transcript
POST /v1/sessions Create a Web/app session
GET /v1/usage Usage breakdown
POST /v1/webhooks/portal Webhook portal link
GET /v1/billing Billing (balance, auto-recharge, per-minute price, payment method)
POST /v1/billing/topups Top-up URL
GET /v1/tools/secret In-call tools signing secret
POST /v1/tools/secret/rotate Rotate the signing secret
GET /v1/connectors List connectors
GET /v1/connections List connections
POST /v1/connections Create a connection (API key or URL)
GET /v1/connections/{id} Get a connection
PATCH /v1/connections/{id} Configure a connection
GET /v1/connections/{id}/options Options for configuring a connection
DELETE /v1/connections/{id} Delete a connection
GET /v1/tenants/{id}/connections List a tenant's connections
POST /v1/tenants/{id}/connect-links Connect-page link for a business

Voice connections (no API key):

Kind URL Description
WebSocket wss://voice.voicast.jp/ws Phone (Twilio Media Streams)
POST https://voice.voicast.jp/v1/webrtc/offer Web and apps (WebRTC offer)
PATCH https://voice.voicast.jp/v1/webrtc/offer Add ICE candidates

For MCP (https://api.voicast.jp/mcp), see MCP.


Create an agent#

POST /v1/agents
Input Type Description
name string Name (1–100 chars)
config AgentConfig The agent config (Agents)

Returns 201 Agent (with config, version: 1). Invalid config returns 422 invalid_config with per-item details.

ts
type Agent = { id: string; name: string; version: number; config?: AgentConfig; created_at: number; updated_at: number };

type AgentConfig = {
  greeting: string;            // first thing the AI says (up to 300 chars)
  persona: string;             // role and manner (up to 1,000 chars)
  fields: { key: string; label: string; required?: boolean; type?: 'text' | 'phone' }[]; // 1–20
  handoff_when: string[];      // handoff rules (up to 20)
  closing: string;             // closing when everything is collected (up to 300 chars)
  closing_answered?: string;   // closing when an FAQ answer settled the call
  faq?: { q: string; a: string }[];  // up to 50
  domain?: string;             // line of business (recognition hint)
  vocabulary?: string[];       // terms (recognition hints, up to 200)
  voice?: string;              // voice name (default Aoede)
  speaking_rate?: number;      // speaking rate (default 1.15)
  tools?: AgentTool[];         // tools (up to 10)
  connectors?: ConnectorEntry[]; // connections (up to 10)
};

type AgentTool = {
  name: string; description: string; url: string; confirm?: boolean;
  parameters?: { properties: Record<string, { type: 'string' | 'number' | 'integer' | 'boolean'; description?: string; enum?: string[] }>; required?: string[] };
};

type ConnectorEntry =
  | { connector: string; actions: string[]; confirm?: string[] }   // a preset
  | { connection: string; actions: string[]; confirm?: string[] }; // a specific connection (con_…)

List agents#

GET /v1/agents

Returns { data: Agent[] } (without config).

Get an agent#

GET /v1/agents/{id}?version=

Omit version (an integer ≥ 1) for the current version. Returns Agent with config; 404 if not found.

Update an agent#

PUT /v1/agents/{id}
Input Type Description
name string Optional. Unchanged if omitted
config AgentConfig The whole config

Returns Agent (the new version). Calls in progress keep the version they started with.

List agent versions#

GET /v1/agents/{id}/versions

Returns { data: [{ version, created_at }] }, newest first.

Delete an agent#

DELETE /v1/agents/{id}

Returns 204. 409 in_use if a tenant uses it. Call records are kept.

Create a tenant#

POST /v1/tenants
Input Type Description
external_id string Your customer ID. 1–100 chars of letters, digits and . _ : -; unique within the environment
name string Optional. Display name (up to 100 chars)
agent_id string Optional. Default agent

Returns 201 Tenant plus secret (the connection secret vts_…, returned only here). 409 conflict if the external_id exists.

ts
type Tenant = { id: string; external_id: string; name: string; agent_id: string | null; status: 'active' | 'disabled'; created_at: number };

List tenants#

GET /v1/tenants

Returns { data: Tenant[] }.

Get a tenant#

GET /v1/tenants/{id}

Returns Tenant (the connection secret is never returned here).

Update a tenant#

PATCH /v1/tenants/{id}
Input Type Description
name string Optional
agent_id string | null Optional. null removes it
status active, disabled Optional. disabled stops phone and Web/app connections

Returns Tenant. Only the fields you pass change.

Rotate a tenant secret#

POST /v1/tenants/{id}/secret

Returns { secret, previous_secret_expires_at }. The old secret keeps working for 24 hours.

Delete a tenant#

DELETE /v1/tenants/{id}

Returns 204. The tenant can no longer connect and its connections are revoked. Call records are kept. You can create a tenant with the same external_id again.

List calls#

GET /v1/calls?tenant_id=&cursor=&limit=
Query Description
tenant_id Optional. Filter by tenant
cursor Optional. Where to continue (pass the previous response's next_cursor as is)
limit Optional. 1–100 (default 50)
before Optional. The older way to page (calls that started before this started_at). It can skip calls that started in the same millisecond, so use cursor. Cannot be combined with cursor (422)

Returns { data: Call[], has_more, next_cursor }, newest first. While has_more is true, pass next_cursor as cursor to read on (next_cursor is null when has_more is false).

ts
type Call = {
  id: string; tenant_id: string; agent: { id: string; version: number };
  provider_call_id: string | null;   // Twilio CallSid, or the session ID for Web and apps
  from: string | null;               // caller's number (phone, when passed)
  started_at: number; ended_at: number | null; duration_ms: number | null;
  outcome: 'completed' | 'answered' | 'handoff' | 'hangup' | 'error' | null; reason: string | null;
  fields: Record<string, string> | null;
  summary: string | null; category: string | null; has_transcript: boolean;
  channel: 'phone' | 'web'; output: 'audio' | 'text';
  billed_yen: number | null;         // what this call costs you (yen before tax, up to 3 decimals); null while in progress
};

billed_yen is the amount taken from the balance for prepaid organizations (seconds × per-minute price ÷ 60), and seconds × the contract per-minute price ÷ 60 for invoiced organizations. Invoices round the month's total seconds up to whole minutes, so the sum of the calls can differ slightly (Pricing and billing).

Get a call#

GET /v1/calls/{id}

Returns Call.

Get a call by provider ID#

GET /v1/calls/by-provider/{providerCallId}

Look up a call by Twilio CallSid (CA…) or session ID (sess_…), for example from Twilio's <Connect> action. Returns Call; 404 if there is no record.

Get a transcript#

GET /v1/calls/{id}/transcript

Returns { call_id, turns: [{ role: 'assistant' | 'user', text }] }. Created after the call ends; 404 if none.

Create a session#

POST /v1/sessions
Input Type Description
tenant_id string Tenant ID
agent_id string Optional. Defaults to the tenant's agent
output audio, text Optional. Default audio

Returns 201 { id, tenant_id, agent_id, output, client_token, expires_at, webrtc_url, ice_servers }. client_token (vct_…) lasts 2 minutes and works once. Pass ice_servers ([{ urls, username?, credential? }]) to RTCPeerConnection's iceServers as is (STUN, plus short-lived TURN when available). Give only client_token, webrtc_url and ice_servers to the browser or app.

Errors: 402 (balance_depleted, payment_overdue, spending_cap_reached), 409 tenant_disabled, 422 no_agent or invalid_request.

Get usage#

GET /v1/usage?from=&to=&group_by=
Query Description
from, to A date (YYYY-MM-DD, Japan time; to includes that day) or Unix milliseconds (to exclusive). Defaults to the start of this month until now. Up to 366 days
group_by Optional. tenant, day or channel

Returns:

ts
type Usage = {
  environment: 'live' | 'test'; from: number; to: number;
  group_by: 'tenant' | 'day' | 'channel' | null; unit_price_yen: number;
  total: { calls: number; seconds: number; minutes: number; amount_yen: number };
  data: (({ tenant: { id: string; external_id: string; name: string } } | { day: string } | { channel: 'phone' | 'web' })
        & { calls: number; seconds: number; minutes: number; amount_yen: number })[];  // [] without group_by
  note: string;
};

Calls that ended within the period are counted. amount_yen is an estimate excluding tax (Pricing and billing).

POST /v1/webhooks/portal
Input Type Description
frame_origin string Optional. Parent origin when embedding in an iframe
locale ja, en Optional. Page language (default ja)

Returns 201 { url, expires_at } (valid for 15 minutes): a page to register endpoint URLs, choose events and see signing secrets (Results). 503 unavailable if webhook delivery is not available.

Get billing#

GET /v1/billing

Returns the billing state of the API key's organization (shared by the live and test environments), the same as Billing in the dashboard.

ts
type Billing = {
  organization_id: string; month: string;            // this month (Japan time, YYYY-MM)
  contract: { payment_method: 'card' | 'invoice'; per_minute_yen: number; invoice_days_until_due: number };
  has_card: boolean;                                   // a card is saved for auto-recharge
  usage: { calls: number; seconds: number; minutes: number };  // this month's calls (live and test)
  estimate_yen: number;                                // prepaid: taken this month; invoice: the estimate
  balance: { yen: number; milli_yen: number; depleted: boolean; next_expiry: { yen: number; at: number } | null } | null;  // prepaid only
  auto_recharge: { enabled: boolean; threshold_yen: number; amount_yen: number; monthly_cap_yen: number | null;
                   recharged_this_month_yen: number; failures: number; max_failures: number } | null;  // prepaid only
  topup: { min_yen: number; max_yen: number; quick_yen: number[]; credit_months: number };
  spending_cap_yen: number | null; cap_reached: boolean;
  payment: { failed_at: number; grace_ends_at: number; suspended: boolean } | null;  // late invoice payment
  tax: 'excluded'; tax_rate: number;
  // also price, stripe_enabled, subscription_status, threshold
};

Create a top-up#

POST /v1/billing/topups
Input Type Description
amount_yen integer Amount in yen before tax, 500–300,000

Returns 201 { url, amount_yen, tax_yen }. url is a Stripe payment page: open it in a browser and pay, and the amount is added to the balance. 422 outside the range, 409 invoice_billing for invoiced organizations. Auto-recharge, the monthly cap and the card are changed in the dashboard (Pricing and billing).

Get the tools signing secret#

GET /v1/tools/secret

Returns { secret, previous_secret_expires_at }. secret (whsec_…) signs the environment's in-call tool requests. previous_secret_expires_at is when the previous secret stops being used after a rotation (null if none).

Rotate the tools signing secret#

POST /v1/tools/secret/rotate

Returns { secret, previous_secret_expires_at }. For 24 hours after a rotation, requests are signed with both the new and the previous secret; switch your server to the new one within that time.

List connectors#

GET /v1/connectors

Returns { data: Connector[] }.

ts
type Connector = {
  id: string; name: string; description: string;
  category: 'scheduling' | 'crm' | 'notify' | 'custom'; auth: 'oauth2' | 'api_key' | 'webhook_url';
  available: boolean;      // usable on this voicast
  needs_setup: boolean;    // needs configuration after connecting
  notify: boolean;         // posts a notification after each call
  env_only: boolean;       // environment connections only (not on the embeddable connect page)
  inputs: { name: string; type: 'text' | 'password' | 'url' | 'number' }[];
  actions: { name: string; tool: string; description: string; confirm: boolean }[]; // tool is the name shown to the AI
};

List connections#

GET /v1/connections?tenant_id=

Without tenant_id: all connections. With an empty value (?tenant_id=): environment connections only. With an ID: that tenant's connections. Deleted connections are not listed. Returns { data: Connection[] }.

ts
type Connection = {
  id: string; connector: string; tenant_id: string | null;
  label: string;           // the provider account (clinic@example.com, workspace #channel and so on)
  status: 'active' | 'needs_setup' | 'needs_reauth';
  config: object; created_by: string | null; created_at: number; updated_at: number;
};

Create a connection (API key or URL)#

POST /v1/connections
Input Type Description
connector teams, shannon_booking, http Connectors that take an API key or URL
secret string The API key or webhook URL (1–2,000 chars)
input object Optional. { base_url } for shannon_booking, { base_url, header } for http
tenant_id string Optional. Makes it a tenant connection. shannon_booking and http are environment-only: passing it returns 422 invalid_request

Returns 201 Connection. voicast makes one test call before saving; failure returns 422 verify_failed. OAuth connectors are connected through a connect-page link or the dashboard.

Get a connection#

GET /v1/connections/{id}

Returns Connection.

Configure a connection#

PATCH /v1/connections/{id}
Input Type Description
config object Connector-specific settings. Only the fields you pass change

Returns Connection. A needs_setup connection becomes active. Invalid settings return 422 invalid_config. See Connections for each connector's settings.

Connection options#

GET /v1/connections/{id}/options

Asks the provider for the choices available when configuring.

Connector Query Returns
Google Calendar, Microsoft 365 { calendars: [{ id, name, primary }] }
Google Sheets spreadsheet, sheet { spreadsheets, sheets, headers }
kintone app { apps, fields: [{ code, label }] }
Chatwork { rooms }
LINE WORKS { users }
MCP server { tools }

409 needs_reauth if the connection is broken.

Delete a connection#

DELETE /v1/connections/{id}

Returns 204 and revokes the provider's token.

List a tenant's connections#

GET /v1/tenants/{id}/connections

Returns { data: Connection[] }.

POST /v1/tenants/{id}/connect-links
Input Type Description
connectors string[] Optional. Connectors to show (google_calendar, m365_calendar, google_sheets, kintone, hubspot, slack, chatwork, lineworks, teams)
frame_origin string Optional. https parent origin when embedding in an iframe

Returns 201 { url, expires_at } (valid for 15 minutes). See Connections.


Phone (Twilio Media Streams)#

wss://voice.voicast.jp/ws

The target of Twilio's <Connect><Stream>. Pass these as <Parameter>s:

Parameter Description
tenant Tenant ID
secret The tenant's connection secret (vts_…)
agent Optional. Agent ID
from Optional. The caller's number

The stream is closed immediately when the secret is wrong, the tenant is disabled or calls are refused for billing reasons. See Phone.

WebRTC offer#

POST https://voice.voicast.jp/v1/webrtc/offer
Input Type Description
sdp, type string The WebRTC offer
token string The session's client_token (Pipecat clients may send it as requestData.token)
pc_id, restart_pc string, boolean Renegotiation only (no token needed)

Returns { sdp, type, pc_id }. 403 when the token is wrong, expired or used, or when calls are refused for billing reasons (billing refusals will later return 402, like POST /v1/sessions).

Once connected, send RTVI client-ready on the data channel, then a ping string every second (Web and apps).

ICE candidates#

PATCH https://voice.voicast.jp/v1/webrtc/offer
Input Type Description
pc_id string pc_id from the offer response
candidates array [{ candidate, sdp_mid, sdp_mline_index }]

Returns { ok: true }. 404 for an unknown pc_id.