Skip to content

Calls

Create WebRTC or PSTN call sessions, add participants, and retrieve post-call data from a single API surface.

Create a Call

POST/v1/calls

Every call request includes one or more participants. Use transport: "webrtc" for browser users and transport: "pstn" for phone numbers.

Rymi returns 201 for WebRTC-only calls and 202 when at least one PSTN participant is queued.

For high-volume PSTN fanout, see POST /v1/calls/batch which accepts up to 500 outbound recipients.

Request Body

FieldTypeRequiredDefaultDescription
agent_iduuidYesAgent to run for this call
participantsParticipant[]YesOne or more call participants
metadataobjectNo{}Custom key-value data stored on the call record
variablesobjectNo{}Variables passed into queued PSTN jobs (accessible in agent prompt templates)
post_callPost-call objectNoAgent defaultPer-call override for post-call intelligence. Merged with the agent's default post_call config

Participant Object

FieldTypeRequiredDefaultDescription
transportstringYeswebrtc for browser participants, pstn for phone numbers
identitystringYesBrowser identity string for WebRTC, or an E.164 phone number (e.g., +15551234567) for PSTN
from_numberstringNoTenant defaultCaller ID for PSTN legs. Optional when a default number can be resolved from the tenant
metadataobjectNo{}Participant-scoped metadata

Example: WebRTC Call

bash
curl -X POST https://api.rymi.live/v1/calls \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "550e8400-e29b-41d4-a716-446655440000",
    "participants": [
      { "transport": "webrtc", "identity": "browser-tab-1" }
    ],
    "metadata": { "source": "sdk", "session_id": "sess_123" }
  }'
ts
const call = await rymi.calls.create({
  agent_id: "550e8400-e29b-41d4-a716-446655440000",
  participants: [{ transport: "webrtc", identity: "browser-tab-1" }],
  metadata: { source: "sdk", session_id: "sess_123" },
});
python
call = rymi.calls.create(
    agent_id="550e8400-e29b-41d4-a716-446655440000",
    participants=[{"transport": "webrtc", "identity": "browser-tab-1"}],
    metadata={"source": "sdk", "session_id": "sess_123"},
)

Response 201

json
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "room_name": "call_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "connecting",
  "participants": [
    {
      "id": "participant_1",
      "transport": "webrtc",
      "identity": "browser-tab-1",
      "status": "joining",
      "access": {
        "url": "wss://livekit.rymi.live",
        "token": "eyJ..."
      }
    }
  ],
  "agent": { "id": "550e8400-...", "name": "Aria" }
}

Example: PSTN Call

bash
curl -X POST https://api.rymi.live/v1/calls \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "550e8400-e29b-41d4-a716-446655440000",
    "participants": [
      {
        "transport": "pstn",
        "identity": "+15551234567",
        "from_number": "+15559876543"
      }
    ],
    "metadata": { "customer_name": "Alice" },
    "variables": { "customer_segment": "renewal" }
  }'
ts
const call = await rymi.calls.create({
  agent_id: "550e8400-e29b-41d4-a716-446655440000",
  participants: [
    {
      transport: "pstn",
      identity: "+15551234567",
      from_number: "+15559876543",
    },
  ],
  metadata: { customer_name: "Alice" },
  variables: { customer_segment: "renewal" },
});
python
call = rymi.calls.create(
    agent_id="550e8400-e29b-41d4-a716-446655440000",
    participants=[
        {
            "transport": "pstn",
            "identity": "+15551234567",
            "from_number": "+15559876543",
        }
    ],
    metadata={"customer_name": "Alice"},
    variables={"customer_segment": "renewal"},
)

Response 202

json
{
  "id": "call_abc123",
  "room_name": "call_call_abc123",
  "status": "queued",
  "participants": [
    {
      "id": "participant_1",
      "transport": "pstn",
      "identity": "+15551234567",
      "status": "queued",
      "telephony_leg_id": "leg_123",
      "job_id": "job_xyz"
    }
  ]
}

Example: Post-call Override

Override the agent's default post-call intelligence for a specific call:

bash
curl -X POST https://api.rymi.live/v1/calls \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "550e8400-e29b-41d4-a716-446655440000",
    "participants": [
      { "transport": "webrtc", "identity": "browser-user" }
    ],
    "post_call": {
      "summary": { "enabled": true },
      "structured_extraction": {
        "json_schema": {
          "type": "object",
          "properties": {
            "booked_appointment": { "type": "boolean" },
            "preferred_date": { "type": "string" }
          }
        }
      },
      "evaluation": {
        "rubric": "1. Did the agent successfully book an appointment?"
      }
    }
  }'
ts
const call = await rymi.calls.create({
  agent_id: "550e8400-e29b-41d4-a716-446655440000",
  participants: [{ transport: "webrtc", identity: "browser-user" }],
  post_call: {
    summary: { enabled: true },
    structured_extraction: {
      json_schema: {
        type: "object",
        properties: {
          booked_appointment: { type: "boolean" },
          preferred_date: { type: "string" },
        },
      },
    },
    evaluation: {
      rubric: "1. Did the agent successfully book an appointment?",
    },
  },
});
python
call = rymi.calls.create(
    agent_id="550e8400-e29b-41d4-a716-446655440000",
    participants=[{"transport": "webrtc", "identity": "browser-user"}],
    post_call={
        "summary": {"enabled": True},
        "structured_extraction": {
            "json_schema": {
                "type": "object",
                "properties": {
                    "booked_appointment": {"type": "boolean"},
                    "preferred_date": {"type": "string"},
                },
            }
        },
        "evaluation": {
            "rubric": "1. Did the agent successfully book an appointment?",
        },
    },
)

Agent defaults are merged with post_call. The resolved config is snapped onto the call record at creation time.

Errors

StatusMeaning
400Validation error (missing agent_id, invalid participants, etc.)
400DNC conflict: one or more PSTN participants are on the Do-Not-Call list
401Missing or invalid API key
402Insufficient credits
403Publishable key scope violation (e.g., phone channel not allowed)
404Agent not found for this tenant
409Agent is not publish-ready (code: agent_not_publish_ready): resolve publish validation blockers before starting live calls
422Provider key unavailable (code: provider_key_unavailable): the agent's LLM/STT/TTS provider key is not configured
423Agent is paused by an administrator (code: agent_admin_paused)
503Platform calls are disabled (code: platform_calls_disabled)

List Calls

GET/v1/calls

Returns a paginated list of call records for the authenticated tenant.

Query Parameters

ParameterTypeDefaultDescription
limitinteger200Max records to return (max 1000)
offsetinteger0Records to skip
cursorstringResume from a previous call ID when traversing call history
statusstringFilter by call status: queued, ringing, in_progress, completed, failed, no_answer, busy, or canceled
agent_iduuidFilter to calls for a single agent

Example Request

bash
curl "https://api.rymi.live/v1/calls?limit=50&status=completed" \
  -H "Authorization: Bearer YOUR_API_KEY"
ts
const { calls } = await rymi.calls.list({ limit: 50, status: "completed" });
python
result = rymi.calls.list(limit=50, status="completed")

Response 200

json
{
  "calls": [
    {
      "id": "call_abc123",
      "room_name": "call_call_abc123",
      "agent_id": "550e8400-...",
      "status": "completed",
      "started_at": "2026-03-01T10:00:00Z",
      "ended_at": "2026-03-01T10:05:00Z",
      "bill_duration": 300,
      "billable_seconds": 300,
      "total_cost": 0.30,
      "provider_cost": 0.18,
      "provider_cost_breakdown": { "llm": 0.05, "stt": 0.04, "tts": 0.09 },
      "provider_cost_calculated_at": "2026-03-01T10:05:10Z",
      "end_reason": "participant_disconnected",
      "metadata": { "batch_id": "batch_1710" },
      "intelligence_status": "completed",
      "participant_count": 1,
      "primary_participant": {
        "identity": "+15551234567",
        "transport": "pstn"
      },
      "participants": [
        { "id": "cp_1", "role": "caller", "transport": "pstn", "identity": "+15551234567", "status": "completed" }
      ]
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0,
  "next_cursor": null
}

end_reason (also on the Call Detail response) is an open string, not a closed enum — there is no database constraint on it, and PSTN calls report carrier-specific values. Treat unknown values gracefully rather than exhaustively matching.

Common values:

GroupValues
Clean endsparticipant_disconnected, client_disconnected, normal_hangup, natural_end, end_call_tool, agent_ended_call, completed
Limits and silencetime_limit_exceeded, max_call_duration, silence_timeout, silence_dead_air, demo_idle_timeout
Voicemailvoicemail_detected, voicemail_left
Failuresinsufficient_funds, no_provider_connected, room_setup_failed, room_connection_failed, session_init_failed, session_start_failed, worker_error, session_error, server_shutdown
Reaper backstopreaper_in_progress_timeout, reaper_post_dial_timeout, reaper_queued_timeout
Carrier (PSTN)<carrier>_<status> — e.g. twilio_completed, plivo_no-answer, vonage_rejected, telnyx_hangup

Errors

StatusMeaning
401Missing or invalid API key
403Publishable keys cannot list call history

List Active Calls

GET/v1/calls/active

Returns the same payload shape as GET /v1/calls, filtered to calls with in_progress status.

Example Request

bash
curl https://api.rymi.live/v1/calls/active \
  -H "Authorization: Bearer YOUR_API_KEY"
ts
const { calls } = await rymi.calls.active();
python
result = rymi.calls.active()

Errors

StatusMeaning
401Missing or invalid API key

Add Participants

POST/v1/calls/:id/participants

Bring a browser participant into an existing PSTN call, or queue another PSTN leg on an active session.

Path Parameters

ParameterTypeDescription
iduuidCall ID

Request Body

FieldTypeRequiredDescription
participantsParticipant[]YesOne or more participants to add

Example Request

bash
curl -X POST https://api.rymi.live/v1/calls/call_abc123/participants \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "participants": [
      { "transport": "webrtc", "identity": "browser-tab-2" }
    ]
  }'
ts
const result = await rymi.calls.addParticipants("call_abc123", {
  participants: [{ transport: "webrtc", identity: "browser-tab-2" }],
});
python
result = rymi.calls.add_participants(
    "call_abc123",
    participants=[{"transport": "webrtc", "identity": "browser-tab-2"}],
)

Errors

StatusMeaning
400Invalid participant data
401Missing or invalid API key
403Publishable keys cannot add call participants, or the call does not belong to this tenant
404Call not found
409Agent is not publish-ready (code: agent_not_publish_ready)

Call Detail

GET/v1/calls/:id

Returns the expanded view of a call including participants, transcript, recording metadata, intelligence results, and event log.

Path Parameters

ParameterTypeDescription
iduuidCall ID

Example Request

bash
curl https://api.rymi.live/v1/calls/call_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY"
ts
const call = await rymi.calls.retrieve("call_abc123");
python
call = rymi.calls.retrieve("call_abc123")

Response 200

json
{
  "id": "call_abc123",
  "status": "completed",
  "intelligence_status": "completed",
  "bill_duration": 145,
  "total_cost": 0.21,
  "transcript": {
    "text": "Caller: Hello\nAgent: Hi there, how can I help?",
    "segments": [
      {
        "speaker": "user",
        "text": "Hello",
        "sequence": 0,
        "started_at_ms": 0,
        "ended_at_ms": 900,
        "is_final": true,
        "source": "runtime"
      },
      {
        "speaker": "agent",
        "text": "Hi there, how can I help?",
        "sequence": 1,
        "started_at_ms": 1000,
        "ended_at_ms": 2500,
        "is_final": true,
        "source": "runtime"
      }
    ],
    "raw": [
      {
        "id": "t_1",
        "type": "user_input",
        "response": "Hello"
      }
    ]
  },
  "recording": {
    "id": "rec_1",
    "status": "completed",
    "format": "wav",
    "provider_recording_id": "prov_123",
    "metadata": {
      "stereo_mix": {
        "signed_url": "https://storage.rymi.live/recordings/..."
      }
    }
  },
  "intelligence": {
    "status": "completed",
    "summary": "Caller asked for support and the agent resolved the issue.",
    "sentiment": "neutral",
    "structured_data": {
      "customer_intent": "support"
    },
    "evaluation": {
      "passed": true,
      "score": 1,
      "reasoning": "The issue was resolved successfully."
    },
    "errors": []
  },
  "events": [
    {
      "id": "evt_1",
      "level": "info",
      "source": "post_call",
      "message": "Post-call analysis completed."
    }
  ]
}

Errors

StatusMeaning
401Missing or invalid API key
403Publishable keys cannot fetch call details
404Call not found

Call Summary

GET/v1/calls/:id/summary

Returns the persisted post-call summary and sentiment. It serves previously computed results and runs no LLM analysis at request time.

Path Parameters

ParameterTypeDescription
iduuidCall ID

Example Request

bash
curl https://api.rymi.live/v1/calls/call_abc123/summary \
  -H "Authorization: Bearer YOUR_API_KEY"
ts
const summary = await rymi.calls.summary("call_abc123");
python
summary = rymi.calls.summary("call_abc123")

Response 200

json
{
  "status": "completed",
  "sentiment": "positive",
  "summary": "The caller confirmed the appointment and no follow-up was needed."
}

Errors

StatusMeaning
401Missing or invalid API key
404Call not found or summary not yet available

Call Transcript

GET/v1/calls/:id/transcript

Returns the raw and normalized transcript for a completed call.

Path Parameters

ParameterTypeDescription
iduuidCall ID

Example Request

bash
curl https://api.rymi.live/v1/calls/call_abc123/transcript \
  -H "Authorization: Bearer YOUR_API_KEY"
ts
const transcript = await rymi.calls.transcript("call_abc123");
python
transcript = rymi.calls.transcript("call_abc123")

Response 200

json
{
  "transcript": [
    {
      "id": "t_1",
      "type": "user_input",
      "response": "Hello?",
      "speaker": "user",
      "sequence": 0,
      "started_at_ms": 0,
      "ended_at_ms": 850,
      "is_final": true,
      "source": "runtime"
    }
  ],
  "text": "Caller: Hello?",
  "segments": [
    {
      "speaker": "user",
      "text": "Hello?",
      "sequence": 0,
      "started_at_ms": 0,
      "ended_at_ms": 850,
      "is_final": true,
      "source": "runtime"
    }
  ]
}

Transcript Segment Fields

FieldTypeDescription
speakerstringuser or agent
textstringTranscribed text
sequenceintegerZero-based segment order
started_at_msintegerSegment start time in milliseconds from call start
ended_at_msintegerSegment end time in milliseconds from call start
is_finalbooleanWhether this is a final (non-interim) transcript segment
sourcestringTranscript source (e.g., runtime)

Errors

StatusMeaning
401Missing or invalid API key
404Call not found or transcript not yet available

Call Recording

GET/v1/calls/:id/recording

Returns the latest recording metadata plus time-limited signed URLs for audio download.

Path Parameters

ParameterTypeDescription
iduuidCall ID

Example Request

bash
curl https://api.rymi.live/v1/calls/call_abc123/recording \
  -H "Authorization: Bearer YOUR_API_KEY"
ts
const recording = await rymi.calls.recording("call_abc123");
python
recording = rymi.calls.recording("call_abc123")

Response 200

json
{
  "call_id": "call_abc123",
  "recording": {
    "id": "rec_1",
    "status": "completed",
    "format": "wav",
    "provider_recording_id": "prov_123",
    "metadata": {
      "stereo_mix": {
        "signed_url": "https://storage.rymi.live/recordings/..."
      }
    }
  }
}

Recording Status Values

StatusDescription
processingRecording is being captured or finalized (initial state)
completedRecording is available for download
failedRecording processing failed

When recording is not enabled for a call, no recording record exists and the endpoint returns 404.

Errors

StatusMeaning
401Missing or invalid API key
403Publishable keys cannot access recordings
404Call not found or recording not available

End a Call

POST/v1/calls/:id/end

Force-ends an active call by closing the LiveKit room. The call transitions to completed and post-call processing is triggered.

Path Parameters

ParameterTypeDescription
iduuidCall ID

Example Request

bash
curl -X POST https://api.rymi.live/v1/calls/call_abc123/end \
  -H "Authorization: Bearer YOUR_API_KEY"
ts
const result = await rymi.calls.end("call_abc123");
python
result = rymi.calls.end("call_abc123")

Response 200

json
{
  "status": "ended",
  "id": "call_abc123",
  "message": "Call has been terminated."
}

Errors

StatusMeaning
401Missing or invalid API key
403Publishable keys cannot end calls
404Call not found
409Call is already completed or failed

Call Events

GET/v1/calls/:id/events

Returns the event log for a specific call, including lifecycle transitions and post-call processing events. Use it to build real-time dashboards and debug call flows.

Path Parameters

ParameterTypeDescription
iduuidCall ID

Query Parameters

ParameterTypeDefaultDescription
limitinteger50Max records to return (max 200)
offsetinteger0Records to skip

Example Request

bash
curl "https://api.rymi.live/v1/calls/call_abc123/events?limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response 200

json
{
  "events": [
    {
      "id": "evt_1",
      "call_id": "call_abc123",
      "level": "info",
      "source": "lifecycle",
      "message": "Call created",
      "created_at": "2026-03-01T10:00:00Z"
    },
    {
      "id": "evt_2",
      "call_id": "call_abc123",
      "level": "info",
      "source": "lifecycle",
      "message": "PSTN leg connected",
      "created_at": "2026-03-01T10:00:05Z"
    },
    {
      "id": "evt_3",
      "call_id": "call_abc123",
      "level": "info",
      "source": "post_call",
      "message": "Post-call analysis completed.",
      "created_at": "2026-03-01T10:05:30Z"
    }
  ],
  "total": 3,
  "offset": 0,
  "limit": 20
}

Event Fields

FieldTypeDescription
idstringEvent ID
call_idstringAssociated call ID
levelstringinfo, warning, or error
sourcestringEvent source: lifecycle, post_call, telephony, livekit
messagestringHuman-readable event description
created_atstringISO 8601 timestamp

Errors

StatusMeaning
401Missing or invalid API key
403Publishable keys cannot access call events
404Call not found

Reprocess Post-call Intelligence

POST/v1/calls/:id/reprocess

Queues post-call intelligence again for a call. Run it after you change the agent's extraction schema or evaluation criteria and want fresh analysis.

Path Parameters

ParameterTypeDescription
iduuidCall ID

Example Request

bash
curl -X POST https://api.rymi.live/v1/calls/call_abc123/reprocess \
  -H "Authorization: Bearer YOUR_API_KEY"
ts
const result = await rymi.calls.reprocess("call_abc123");
python
result = rymi.calls.reprocess("call_abc123")

Response 202

json
{
  "status": "queued",
  "job_id": "job_123",
  "message": "Post-call intelligence reprocessing has been queued."
}

Errors

StatusMeaning
401Missing or invalid API key
403Publishable keys cannot trigger reprocessing
404Call not found
409Post-call intelligence is already being processed for this call

Queue Stats

GET/v1/calls/queue/stats

Returns the outbound dialing queue counts for PSTN calls.

Example Request

bash
curl https://api.rymi.live/v1/calls/queue/stats \
  -H "Authorization: Bearer YOUR_API_KEY"
ts
const stats = await rymi.calls.queueStats();
python
stats = rymi.calls.queue_stats()

Response 200

json
{
  "waiting": 12,
  "active": 2,
  "completed": 340,
  "failed": 4
}

Publishable Key Rules

When using a publishable key (sb_publishable_...) to create calls:

  • WebRTC: Allowed when the key permits the web channel.
  • PSTN: Allowed only when the key permits the phone channel.
  • Phone limit: Publishable phone calls must target exactly one PSTN participant and cannot override agent_id or from_number.
  • Read restrictions: Publishable keys cannot list call history, fetch recordings, access transcripts, or trigger reprocessing.