Calls
Create WebRTC or PSTN call sessions, add participants, and retrieve post-call data from a single API surface.
Create a Call
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
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | uuid | Yes | — | Agent to run for this call |
participants | Participant[] | Yes | — | One or more call participants |
metadata | object | No | {} | Custom key-value data stored on the call record |
variables | object | No | {} | Variables passed into queued PSTN jobs (accessible in agent prompt templates) |
post_call | Post-call object | No | Agent default | Per-call override for post-call intelligence. Merged with the agent's default post_call config |
Participant Object
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
transport | string | Yes | — | webrtc for browser participants, pstn for phone numbers |
identity | string | Yes | — | Browser identity string for WebRTC, or an E.164 phone number (e.g., +15551234567) for PSTN |
from_number | string | No | Tenant default | Caller ID for PSTN legs. Optional when a default number can be resolved from the tenant |
metadata | object | No | {} | Participant-scoped metadata |
Example: WebRTC Call
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" }
}'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" },
});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
{
"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
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" }
}'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" },
});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
{
"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:
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?"
}
}
}'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?",
},
},
});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
| Status | Meaning |
|---|---|
400 | Validation error (missing agent_id, invalid participants, etc.) |
400 | DNC conflict: one or more PSTN participants are on the Do-Not-Call list |
401 | Missing or invalid API key |
402 | Insufficient credits |
403 | Publishable key scope violation (e.g., phone channel not allowed) |
404 | Agent not found for this tenant |
409 | Agent is not publish-ready (code: agent_not_publish_ready): resolve publish validation blockers before starting live calls |
422 | Provider key unavailable (code: provider_key_unavailable): the agent's LLM/STT/TTS provider key is not configured |
423 | Agent is paused by an administrator (code: agent_admin_paused) |
503 | Platform calls are disabled (code: platform_calls_disabled) |
List Calls
Returns a paginated list of call records for the authenticated tenant.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 200 | Max records to return (max 1000) |
offset | integer | 0 | Records to skip |
cursor | string | — | Resume from a previous call ID when traversing call history |
status | string | — | Filter by call status: queued, ringing, in_progress, completed, failed, no_answer, busy, or canceled |
agent_id | uuid | — | Filter to calls for a single agent |
Example Request
curl "https://api.rymi.live/v1/calls?limit=50&status=completed" \
-H "Authorization: Bearer YOUR_API_KEY"const { calls } = await rymi.calls.list({ limit: 50, status: "completed" });result = rymi.calls.list(limit=50, status="completed")Response 200
{
"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:
| Group | Values |
|---|---|
| Clean ends | participant_disconnected, client_disconnected, normal_hangup, natural_end, end_call_tool, agent_ended_call, completed |
| Limits and silence | time_limit_exceeded, max_call_duration, silence_timeout, silence_dead_air, demo_idle_timeout |
| Voicemail | voicemail_detected, voicemail_left |
| Failures | insufficient_funds, no_provider_connected, room_setup_failed, room_connection_failed, session_init_failed, session_start_failed, worker_error, session_error, server_shutdown |
| Reaper backstop | reaper_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
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
403 | Publishable keys cannot list call history |
List Active Calls
Returns the same payload shape as GET /v1/calls, filtered to calls with in_progress status.
Example Request
curl https://api.rymi.live/v1/calls/active \
-H "Authorization: Bearer YOUR_API_KEY"const { calls } = await rymi.calls.active();result = rymi.calls.active()Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
Add Participants
Bring a browser participant into an existing PSTN call, or queue another PSTN leg on an active session.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | uuid | Call ID |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
participants | Participant[] | Yes | One or more participants to add |
Example Request
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" }
]
}'const result = await rymi.calls.addParticipants("call_abc123", {
participants: [{ transport: "webrtc", identity: "browser-tab-2" }],
});result = rymi.calls.add_participants(
"call_abc123",
participants=[{"transport": "webrtc", "identity": "browser-tab-2"}],
)Errors
| Status | Meaning |
|---|---|
400 | Invalid participant data |
401 | Missing or invalid API key |
403 | Publishable keys cannot add call participants, or the call does not belong to this tenant |
404 | Call not found |
409 | Agent is not publish-ready (code: agent_not_publish_ready) |
Call Detail
Returns the expanded view of a call including participants, transcript, recording metadata, intelligence results, and event log.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | uuid | Call ID |
Example Request
curl https://api.rymi.live/v1/calls/call_abc123 \
-H "Authorization: Bearer YOUR_API_KEY"const call = await rymi.calls.retrieve("call_abc123");call = rymi.calls.retrieve("call_abc123")Response 200
{
"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
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
403 | Publishable keys cannot fetch call details |
404 | Call not found |
Call Summary
Returns the persisted post-call summary and sentiment. It serves previously computed results and runs no LLM analysis at request time.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | uuid | Call ID |
Example Request
curl https://api.rymi.live/v1/calls/call_abc123/summary \
-H "Authorization: Bearer YOUR_API_KEY"const summary = await rymi.calls.summary("call_abc123");summary = rymi.calls.summary("call_abc123")Response 200
{
"status": "completed",
"sentiment": "positive",
"summary": "The caller confirmed the appointment and no follow-up was needed."
}Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
404 | Call not found or summary not yet available |
Call Transcript
Returns the raw and normalized transcript for a completed call.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | uuid | Call ID |
Example Request
curl https://api.rymi.live/v1/calls/call_abc123/transcript \
-H "Authorization: Bearer YOUR_API_KEY"const transcript = await rymi.calls.transcript("call_abc123");transcript = rymi.calls.transcript("call_abc123")Response 200
{
"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
| Field | Type | Description |
|---|---|---|
speaker | string | user or agent |
text | string | Transcribed text |
sequence | integer | Zero-based segment order |
started_at_ms | integer | Segment start time in milliseconds from call start |
ended_at_ms | integer | Segment end time in milliseconds from call start |
is_final | boolean | Whether this is a final (non-interim) transcript segment |
source | string | Transcript source (e.g., runtime) |
Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
404 | Call not found or transcript not yet available |
Call Recording
Returns the latest recording metadata plus time-limited signed URLs for audio download.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | uuid | Call ID |
Example Request
curl https://api.rymi.live/v1/calls/call_abc123/recording \
-H "Authorization: Bearer YOUR_API_KEY"const recording = await rymi.calls.recording("call_abc123");recording = rymi.calls.recording("call_abc123")Response 200
{
"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
| Status | Description |
|---|---|
processing | Recording is being captured or finalized (initial state) |
completed | Recording is available for download |
failed | Recording processing failed |
When recording is not enabled for a call, no recording record exists and the endpoint returns 404.
Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
403 | Publishable keys cannot access recordings |
404 | Call not found or recording not available |
End a Call
Force-ends an active call by closing the LiveKit room. The call transitions to completed and post-call processing is triggered.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | uuid | Call ID |
Example Request
curl -X POST https://api.rymi.live/v1/calls/call_abc123/end \
-H "Authorization: Bearer YOUR_API_KEY"const result = await rymi.calls.end("call_abc123");result = rymi.calls.end("call_abc123")Response 200
{
"status": "ended",
"id": "call_abc123",
"message": "Call has been terminated."
}Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
403 | Publishable keys cannot end calls |
404 | Call not found |
409 | Call is already completed or failed |
Call 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
| Parameter | Type | Description |
|---|---|---|
id | uuid | Call ID |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Max records to return (max 200) |
offset | integer | 0 | Records to skip |
Example Request
curl "https://api.rymi.live/v1/calls/call_abc123/events?limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"Response 200
{
"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
| Field | Type | Description |
|---|---|---|
id | string | Event ID |
call_id | string | Associated call ID |
level | string | info, warning, or error |
source | string | Event source: lifecycle, post_call, telephony, livekit |
message | string | Human-readable event description |
created_at | string | ISO 8601 timestamp |
Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
403 | Publishable keys cannot access call events |
404 | Call not found |
Reprocess Post-call Intelligence
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
| Parameter | Type | Description |
|---|---|---|
id | uuid | Call ID |
Example Request
curl -X POST https://api.rymi.live/v1/calls/call_abc123/reprocess \
-H "Authorization: Bearer YOUR_API_KEY"const result = await rymi.calls.reprocess("call_abc123");result = rymi.calls.reprocess("call_abc123")Response 202
{
"status": "queued",
"job_id": "job_123",
"message": "Post-call intelligence reprocessing has been queued."
}Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
403 | Publishable keys cannot trigger reprocessing |
404 | Call not found |
409 | Post-call intelligence is already being processed for this call |
Queue Stats
Returns the outbound dialing queue counts for PSTN calls.
Example Request
curl https://api.rymi.live/v1/calls/queue/stats \
-H "Authorization: Bearer YOUR_API_KEY"const stats = await rymi.calls.queueStats();stats = rymi.calls.queue_stats()Response 200
{
"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
webchannel. - PSTN: Allowed only when the key permits the
phonechannel. - Phone limit: Publishable phone calls must target exactly one PSTN participant and cannot override
agent_idorfrom_number. - Read restrictions: Publishable keys cannot list call history, fetch recordings, access transcripts, or trigger reprocessing.

