Docs
← chronapilot.com v 2026-05-12
API reference · Voice Realtime
hub_

Voice Realtime

Streaming voice assistant pre-bound to a user's calendar. WebSocket protocol modeled on the OpenAI Realtime API.

Endpoints

POST /v1/voice/sessions Mint a voice session token
WS /v1/voice/realtime Open a realtime voice WebSocket
GET /v1/voice/sessions/{id} Inspect a session
DELETE /v1/voice/sessions/{id} Close a session

Connect

Open a WebSocket to:

text
wss://api.chronapilot.com/v1/voice/realtime?session=<jwt>

The session JWT is scoped to one user. It carries the user ID, the granted scopes, and a short TTL (5 min by default, max 30 min). If you call from a Connect platform key, the JWT additionally carries the cpa_… connected-account ID and inherits that account's restrictions.

Mint the JWT server-side from your sk_live_… key — never embed your secret key in a client:

HTTP
POST /v1/voice/sessions
JSON
{
  "user_id": "usr_1abc2def3ghi",
  "scopes": ["events:read","events:write","alerts:read"],
  "ttl_seconds": 300
}

Response:

JSON
{
  "id": "hub_9stu0vwx1yza",
  "object": "voice_session",
  "session_token": "eyJhbGciOi…",
  "expires_at": "2026-05-22T13:05:00-07:00"
}

Message protocol

Modeled on Azure OpenAI Realtime, with ChronaPilot extensions.

Client → server

  • session.update — set model, voice, tools, output format.
  • audio.append — append PCM16 audio chunk (base64).
  • text.append — append a text user turn.
  • response.cancel — abort current response (barge-in).

Server → client

  • transcript.partial — partial user transcript (low-latency).
  • transcript.final — final user transcript.
  • intent.detected{ name, confidence, parameters }.
  • action.proposed — AI wants to call a tool; client surface for user confirmation.
  • action.executed — tool ran; payload includes the tool result and the affected resource ID.
  • audio.delta — TTS audio chunk to play (base64 PCM16).
  • response.done — turn complete.

Available tools

The model has 26 pre-bound tools — calendar reads, mutations, travel queries, alert management. The full schema mirrors the production tool catalog (CalendarToolDefinitions.cs):

  • create_event, update_event, cancel_event, bulk_cancel_events
  • get_day_overview, get_day_summary, get_weekly_summary
  • get_next_event, search_events
  • get_departure_info, get_travel_time, get_alternate_routes, start_navigation
  • check_if_late, acknowledge_late, report_arrival
  • get_alerts, dismiss_alert, snooze_alert
  • create_reminder
  • notify_attendees
  • update_preferences, set_quiet_hours, set_alert_profile, set_global_buffer
  • check_availability

Confirmation flow

By default, mutating tools (anything that writes to a calendar) emit action.proposed first. Your client renders a confirmation (or auto-confirms based on user preference); on user confirmation, send action.confirm. Read-only tools execute without prompting.

Sample flow

JavaScript
const ws = new WebSocket(`wss://api.chronapilot.com/v1/voice/realtime?session=${sessionToken}`);

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: 'session.update',
    session: { voice: 'sage', output_format: 'pcm16' }
  }));
};

ws.onmessage = (m) => {
  const ev = JSON.parse(m.data);
  switch (ev.type) {
    case 'transcript.final':
      console.log('user said:', ev.text);
      break;
    case 'action.proposed':
      ui.showConfirm(ev.action, () => ws.send(JSON.stringify({ type: 'action.confirm', id: ev.id })));
      break;
    case 'audio.delta':
      audioPlayer.enqueue(base64ToPCM(ev.audio));
      break;
  }
};

Reference implementation