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 |
400 | unsupported_country or invalid_phone_number: a phone number is outside Rymi's supported countries or doesn't parse |
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",
"sentiment": "positive",
"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
}sentiment is the post-call sentiment, null until post-call analysis finishes. The summary is on Call Detail.
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
}List Scheduled Callbacks
Returns the callbacks your agents scheduled for themselves with the Scheduled callbacks tool, latest due time first. The platform places each one at its due time. There is no SDK or MCP wrapper for this endpoint yet.
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | No | - | Only callbacks scheduled by this agent |
status | string | No | - | One of pending, dispatching, placed, no_answer_retry, done, cancelled, failed |
limit | integer | No | 50 | 1 to 200 |
offset | integer | No | 0 | Rows to skip |
Example Request
curl "https://api.rymi.live/v1/scheduled-callbacks?status=pending&limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"Response 200
{
"callbacks": [
{
"id": "6c1f5e0a-2b7d-4c0e-9a61-3d2f8b4e7a10",
"agent_id": "16e0724d-bc5e-4a82-9792-f3ac83c561f0",
"agent_name": "Isha",
"to_number": "+919876543210",
"due_at": "2026-10-06T12:30:00+00:00",
"status": "pending",
"reason": "driving, wants callback",
"notes": "Rohit, budget 50 lakh, Skyline Greens",
"attempts": 0,
"result_call_id": null,
"last_error": null,
"created_at": "2026-10-05T10:00:00+00:00"
}
],
"total": 1,
"offset": 0,
"limit": 50
}| Field | Description |
|---|---|
due_at | When the call is due, in UTC. 2026-10-06T12:30:00+00:00 is 6:00 pm IST |
status | See the statuses in the guide |
attempts | Calls placed so far (0, 1 or 2) |
result_call_id | The call that was placed, once there is one. Fetch it with Call Detail |
last_error | Why it did not go out: no_caller_id, dnc, dnc_lookup_failed, no_answer, busy, or cancelled by user |
Errors
| Status | Meaning |
|---|---|
400 | status is not one of the listed values |
401 | Missing or invalid API key |
403 | Publishable keys cannot read scheduled callbacks |
Cancel a Scheduled Callback
Stops a callback that has not been placed yet. It works while the status is pending or no_answer_retry.
Example Request
curl -X POST https://api.rymi.live/v1/scheduled-callbacks/6c1f5e0a-2b7d-4c0e-9a61-3d2f8b4e7a10/cancel \
-H "Authorization: Bearer YOUR_API_KEY"Response 200
{ "status": "cancelled", "id": "6c1f5e0a-2b7d-4c0e-9a61-3d2f8b4e7a10" }Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
403 | Publishable keys cannot cancel scheduled callbacks |
404 | No callback with that id in your workspace |
409 | The callback is already being placed or has finished, so it cannot be cancelled |
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.

