# Create Agent Folder
Source: https://docs.revring.ai/api-reference/agent-folders/create
POST /agent-folders
Create a new folder for organizing agents. Folder names must be unique within your organization.
# Delete Agent Folder
Source: https://docs.revring.ai/api-reference/agent-folders/delete
DELETE /agent-folders/{id}
Delete a folder. Agents in the folder are moved to Uncategorized (not deleted).
# List Agent Folders
Source: https://docs.revring.ai/api-reference/agent-folders/list
GET /agent-folders
Returns all folders for your organization plus the count of uncategorized agents (agents not in any folder).
# Update Agent Folder
Source: https://docs.revring.ai/api-reference/agent-folders/update
PATCH /agent-folders/{id}
Rename a folder or change its color.
# Add Phone Number
Source: https://docs.revring.ai/api-reference/agents/add-phone-number
POST /agents/{id}/phone-numbers
Assign a phone number to an agent
# Batch Get Agents
Source: https://docs.revring.ai/api-reference/agents/batch-get
POST /agents/batch-get
Fetch multiple agents by ID in a single request. Unknown IDs are omitted from the response. Maximum 100 IDs per request.
# Create Agent
Source: https://docs.revring.ai/api-reference/agents/create
POST /agents
Create a new AI voice agent with specified configuration
# Create Custom Tool
Source: https://docs.revring.ai/api-reference/agents/create-custom-tool
POST /agents/{id}/custom-tools
Add a custom tool that the agent can invoke via function calling
# Delete Agent
Source: https://docs.revring.ai/api-reference/agents/delete
DELETE /agents/{id}
Delete an agent
# Delete Custom Tool
Source: https://docs.revring.ai/api-reference/agents/delete-custom-tool
DELETE /agents/{id}/custom-tools/{toolId}
Remove a custom tool from an agent
# Delete Environment
Source: https://docs.revring.ai/api-reference/agents/delete-environment
DELETE /agents/{id}/environments/{name}
Removes the environment. Refused while inbound numbers are still bound to it (409 environment_in_use): move them first. `production` cannot be deleted.
# Duplicate Agent
Source: https://docs.revring.ai/api-reference/agents/duplicate
POST /agents/{id}/duplicate
Create a copy of an existing agent. Copies all configuration, custom tools, and knowledge base assignments. Phone numbers are not copied and must be assigned to the new agent separately. The new agent's name is the original name with ` (Copy)` appended.
# Get Agent
Source: https://docs.revring.ai/api-reference/agents/get
GET /agents/{id}
Retrieve specific agent by ID
# Get Agent Version
Source: https://docs.revring.ai/api-reference/agents/get-revision
GET /agents/{id}/revisions/{revision}
One version in full: the complete configuration snapshot plus a field-level comparison against the previous version.
# Link Knowledge Base
Source: https://docs.revring.ai/api-reference/agents/link-knowledge-base
POST /agents/{id}/knowledge-bases
Link a knowledge base to an agent. The agent will use the knowledge base for retrieval-augmented generation during calls.
# List Agents
Source: https://docs.revring.ai/api-reference/agents/list
GET /agents
List agents with filtering, sorting, and pagination. Default page size is 25 (maximum 100). To hydrate a known set of agents in one call, use `POST /agents/batch-get` instead.
# List Custom Tools
Source: https://docs.revring.ai/api-reference/agents/list-custom-tools
GET /agents/{id}/custom-tools
Get custom tools configured for an agent
# List Agent Environments
Source: https://docs.revring.ai/api-reference/agents/list-environments
GET /agents/{id}/environments
Environments this agent can run calls in. `production` is always first and reflects the published version. See the Versions and Publishing guide.
# List Agent Knowledge Bases
Source: https://docs.revring.ai/api-reference/agents/list-knowledge-bases
GET /agents/{id}/knowledge-bases
List knowledge bases linked to an agent
# List Agent Phone Numbers
Source: https://docs.revring.ai/api-reference/agents/list-phone-numbers
GET /agents/{id}/phone-numbers
Get phone numbers assigned to an agent for inbound calls
# List Agent Versions
Source: https://docs.revring.ai/api-reference/agents/list-revisions
GET /agents/{id}/revisions
Paginated version history for an agent, newest first. Every configuration change (agent fields, custom tools, phone numbers, knowledge base links) is recorded automatically with who made it and which fields changed. History remains readable after an agent is deleted; the final version is a `deleted` record holding the last configuration.
# Publish Agent
Source: https://docs.revring.ai/api-reference/agents/publish
POST /agents/{id}/publish
Make a version live for new calls. With an empty body the current saved draft becomes the live version and is recorded as a `published` entry in the version history (who published, when). With `{ "revision": N }` an existing version becomes live without changing the draft. In `auto` publish mode every save is already live, so this only records a version. In `manual` mode calls keep using the published version until you publish again.
# Remove Phone Number
Source: https://docs.revring.ai/api-reference/agents/remove-phone-number
DELETE /agents/{id}/phone-numbers/{phoneId}
Remove a phone number from an agent
# Restore Agent Version
Source: https://docs.revring.ai/api-reference/agents/restore-revision
POST /agents/{id}/revisions/{revision}/restore
Set the agent's configuration back to this version. Applies all agent fields and reconciles custom tools to the snapshot, using the same validation as a normal update. Phone numbers and knowledge base links are never changed by a restore. Trunk, messaging connection, and folder references are restored only if they still exist, and any that do not are reported in `skippedStaleRefs`. The restore is recorded as a new `restored` version, so the configuration you had before it stays in the history.
# Unlink Knowledge Base
Source: https://docs.revring.ai/api-reference/agents/unlink-knowledge-base
DELETE /agents/{id}/knowledge-bases/{kbId}
Remove a knowledge base from an agent
# Update Agent
Source: https://docs.revring.ai/api-reference/agents/update
PATCH /agents/{id}
Update agent configuration
# Update Custom Tool
Source: https://docs.revring.ai/api-reference/agents/update-custom-tool
PATCH /agents/{id}/custom-tools/{toolId}
Update custom tool configuration
# Update Phone Number
Source: https://docs.revring.ai/api-reference/agents/update-phone-number
PATCH /agents/{id}/phone-numbers/{phoneId}
Changes which environment inbound calls on this number run on (null for production), or splits them across versions with `trafficSplit`.
# Create or Update Environment
Source: https://docs.revring.ai/api-reference/agents/upsert-environment
PUT /agents/{id}/environments/{name}
Creates the environment when the name is new, otherwise updates the fields you send. Up to 10 environments per agent. `production` cannot be edited here (409 environment_builtin): publish instead. A `revision` must exist in the agent's version history.
# Create Alert Rule
Source: https://docs.revring.ai/api-reference/alerts/create
POST /alert-rules
# Delete Alert Rule
Source: https://docs.revring.ai/api-reference/alerts/delete
DELETE /alert-rules/{id}
# List Alert Events
Source: https://docs.revring.ai/api-reference/alerts/events
GET /alert-rules/{id}/events
Past notifications (newest first, up to 100) with the delivery result for each channel.
# Get Alert Rule
Source: https://docs.revring.ai/api-reference/alerts/get
GET /alert-rules/{id}
# List Alert Rules
Source: https://docs.revring.ai/api-reference/alerts/list
GET /alert-rules
# Send Test Alert
Source: https://docs.revring.ai/api-reference/alerts/test
POST /alert-rules/{id}/test
Sends an `alert.test` notification to the rule's channels with the current value.
# Update Alert Rule
Source: https://docs.revring.ai/api-reference/alerts/update
PATCH /alert-rules/{id}
Changes take effect at the next check (within 5 minutes); a firing rule whose new condition is not met resolves.
# Get Alert Value
Source: https://docs.revring.ai/api-reference/alerts/value
GET /alert-rules/{id}/value
The rule's metric right now. Nothing is sent.
# Get Active Calls Count
Source: https://docs.revring.ai/api-reference/analytics/active
GET /analytics/active
Get the current number of active (ongoing) calls. Not affected by date filters. Also returns `concurrency`: `active` (calls dialing or connected), `limit` (your plan's concurrent calls) and `available`, counted exactly like the check that refuses new calls with 429 concurrency_limit, so a dialer can pace itself.
# Get Analytics Overview
Source: https://docs.revring.ai/api-reference/analytics/overview
GET /analytics/overview
Get analytics overview with time-series data and summary cards
# Compare Versions
Source: https://docs.revring.ai/api-reference/analytics/versions
GET /analytics/versions
An agent's calls grouped by the version they ran on (environment and revision), to compare an A/B split. Default period: the last 7 days; at most 90.
# Batch Get Calls
Source: https://docs.revring.ai/api-reference/calls/batch-get
POST /calls/batch-get
Fetch multiple calls by ID in a single request. Unknown IDs are omitted from the response. Maximum 100 IDs per request.
# Cancel Call
Source: https://docs.revring.ai/api-reference/calls/cancel
POST /calls/{id}/cancel
Cancel a queued call. Only calls with status `QUEUED` can be cancelled.
# End Call
Source: https://docs.revring.ai/api-reference/calls/end
POST /calls/{id}/end
Hang up a call that is in progress, immediately. The caller is disconnected, the call is recorded with hangupCause `API_ENDED`, and the recording, summary, post-call analysis and post-call webhook are produced as for any other call. Use it from your own logic, for example when your system decides the call reached voicemail or should stop.
# Export Calls
Source: https://docs.revring.ai/api-reference/calls/export
GET /calls/export
CSV of calls, newest first, up to 10,000 per file: call details, sentiment, summary, recording URL, one column per call variable (var.*), extracted field (extraction.*) and success criterion result (evaluation.*). Cells that would run as a spreadsheet formula are prefixed with an apostrophe.
# Get Call
Source: https://docs.revring.ai/api-reference/calls/get
GET /calls/{id}
Retrieve call details by ID
# List Calls
Source: https://docs.revring.ai/api-reference/calls/list
GET /calls
List calls with filtering, sorting, and pagination
# Re-run Analysis
Source: https://docs.revring.ai/api-reference/calls/reanalyze
POST /calls/{id}/reanalyze
Re-run extraction, evaluation and caller sentiment on one ended call with the agent's current settings. A field or criterion removed from the agent is cleared on the call.
# Re-run Analysis (bulk)
Source: https://docs.revring.ai/api-reference/calls/reanalyze-bulk
POST /calls/reanalyze
Re-run extraction, evaluation and caller sentiment with the agent's current settings on up to 50 of its ended calls in a time window, newest first. Calls with no caller speech or with deleted content are skipped.
# Send Call
Source: https://docs.revring.ai/api-reference/calls/send
POST /calls
Initiate an outbound call with an agent
# Add Campaign Contacts
Source: https://docs.revring.ai/api-reference/campaigns/add-contacts
POST /campaigns/{id}/contacts
# Cancel Campaign
Source: https://docs.revring.ai/api-reference/campaigns/cancel
POST /campaigns/{id}/cancel
Stop for good; contacts not yet called become canceled.
# Create Campaign
Source: https://docs.revring.ai/api-reference/campaigns/create
POST /campaigns
Create a campaign, optionally with contacts (up to 10,000) and optionally started at once with `start: true`. Duplicate numbers are skipped.
# Delete Campaign
Source: https://docs.revring.ai/api-reference/campaigns/delete
DELETE /campaigns/{id}
Delete a draft, completed or canceled campaign and its contacts. Cancel a running campaign first. Calls it placed are kept.
# Export Campaign Results
Source: https://docs.revring.ai/api-reference/campaigns/export
GET /campaigns/{id}/export
CSV with one row per contact and its last call: status, outcome, attempts, duration, summary, recording, `var.*`, `extraction.*` and `evaluation.*` columns.
# Get Campaign
Source: https://docs.revring.ai/api-reference/campaigns/get
GET /campaigns/{id}
# List Campaigns
Source: https://docs.revring.ai/api-reference/campaigns/list
GET /campaigns
# List Campaign Contacts
Source: https://docs.revring.ai/api-reference/campaigns/list-contacts
GET /campaigns/{id}/contacts
# Pause Campaign
Source: https://docs.revring.ai/api-reference/campaigns/pause
POST /campaigns/{id}/pause
Stop new dials. Calls in progress finish and are recorded.
# Start Campaign
Source: https://docs.revring.ai/api-reference/campaigns/start
POST /campaigns/{id}/start
Start a draft campaign or resume a paused one.
# Update Campaign
Source: https://docs.revring.ai/api-reference/campaigns/update
PATCH /campaigns/{id}
Change settings of a campaign that is not completed or canceled. Takes effect on the next dial.
# Delete Conversation
Source: https://docs.revring.ai/api-reference/conversations/delete
DELETE /conversations/{id}
Clear a conversation thread and its logged messages, so the next text from that contact starts fresh. Use it to re-test an agent as a new contact, or to forget a contact.
# List Conversations
Source: https://docs.revring.ai/api-reference/conversations/list
GET /conversations
List managed conversation threads (one per contact per agent number), newest activity first. Offset-paginated.
# Add Do-Not-Call Numbers
Source: https://docs.revring.ai/api-reference/do-not-call/add
POST /do-not-call
Campaigns never dial these numbers.
# List Do-Not-Call Numbers
Source: https://docs.revring.ai/api-reference/do-not-call/list
GET /do-not-call
# Remove Do-Not-Call Number
Source: https://docs.revring.ai/api-reference/do-not-call/remove
DELETE /do-not-call/{e164}
# Import Agents
Source: https://docs.revring.ai/api-reference/imports/import
POST /imports
Import agents from Retell AI or ElevenLabs Agents. A dry run (the default) returns a per-setting report and creates nothing. With `dryRun: false` the agents are created with their custom tools and knowledge bases. Importing an agent again updates the agent it created before.
See [Import Agents](/platform/importing-agents) for what carries over and how to read the report.
# List Source Agents
Source: https://docs.revring.ai/api-reference/imports/list-agents
POST /imports/agents
List the voice agents on your Retell AI or ElevenLabs account, to choose which to import.
# Introduction
Source: https://docs.revring.ai/api-reference/introduction
How to access and use the RevRing API
You can use the RevRing API in automation tools such as N8N, Make, GoHighLevel, and more to automate any kind of call sequence or logic!
## Creating an API Key
To begin using the RevRing API, you must first create an API Key from the dashboard.
API Keys are sensitive credentials, so ensure to store them securely and limit access.
Create a new API Key from the RevRing Dashboard
## Authentication
All API endpoints are authenticated using the `x-api-key` header:
```bash theme={null}
curl --request GET \
--url https://api.revring.ai/v1/agents \
--header 'x-api-key: '
```
# Add Source
Source: https://docs.revring.ai/api-reference/knowledge-bases/add-source
POST /knowledge-bases/{id}/sources
Add a source to a knowledge base. Supports three source types:
- **Text**: Send JSON with `sourceType: "text"`, `displayName`, and `content`
- **URL**: Send JSON with `sourceType: "url"` and `originalUrl`; add `crawl` to read linked pages of the same site and/or refresh on a schedule
- **File**: Send `multipart/form-data` with a `file` field (max 20MB)
Maximum of 50 sources per knowledge base. Sources are processed asynchronously.
# Create Knowledge Base
Source: https://docs.revring.ai/api-reference/knowledge-bases/create
POST /knowledge-bases
Create a new knowledge base. After creation, add sources (text, URL, or file) to populate it.
# Delete Knowledge Base
Source: https://docs.revring.ai/api-reference/knowledge-bases/delete
DELETE /knowledge-bases/{id}
Delete a knowledge base and all its sources. This also removes all agent links.
# Delete Source
Source: https://docs.revring.ai/api-reference/knowledge-bases/delete-source
DELETE /knowledge-bases/{id}/sources/{sourceId}
Remove a source from a knowledge base
# Get Knowledge Base
Source: https://docs.revring.ai/api-reference/knowledge-bases/get
GET /knowledge-bases/{id}
Retrieve a knowledge base by ID, including its sources
# Get Source
Source: https://docs.revring.ai/api-reference/knowledge-bases/get-source
GET /knowledge-bases/{id}/sources/{sourceId}
Get a specific source and its processing status
# List Knowledge Bases
Source: https://docs.revring.ai/api-reference/knowledge-bases/list
GET /knowledge-bases
List all knowledge bases for your organization
# Refresh Source
Source: https://docs.revring.ai/api-reference/knowledge-bases/refresh-source
POST /knowledge-bases/{id}/sources/{sourceId}/refresh
Read a URL source again now, with its crawl settings. Runs in the background. Agents keep using the current content until the new content is fully indexed; unchanged content is not replaced, and a failed read keeps the current content.
# Update Knowledge Base
Source: https://docs.revring.ai/api-reference/knowledge-bases/update
PATCH /knowledge-bases/{id}
Update knowledge base name or description
# Get Message
Source: https://docs.revring.ai/api-reference/messages/get
GET /messages/{id}
Retrieve a single message from your organization's log by ID.
# List Messages
Source: https://docs.revring.ai/api-reference/messages/list
GET /messages
Retrieve the message log for your organization, newest first. Offset-paginated.
# Send Message
Source: https://docs.revring.ai/api-reference/messages/send
POST /messages
Send an SMS. Provide either a raw `body` or set `generate` to true to compose the message with AI from the agent's SMS prompt and optional inline history. Sends through your connected provider.
# Create Messaging Connection
Source: https://docs.revring.ai/api-reference/messaging-connections/create
POST /messaging-connections
Connect a messaging provider. Credentials are verified with the provider before the connection is saved. The Auth Token is stored securely and is never returned.
# Delete Messaging Connection
Source: https://docs.revring.ai/api-reference/messaging-connections/delete
DELETE /messaging-connections/{id}
Delete a messaging connection. Cannot delete if agents are still using it.
# Get Messaging Connection
Source: https://docs.revring.ai/api-reference/messaging-connections/get
GET /messaging-connections/{id}
Retrieve a specific messaging connection by ID. Secrets are never returned.
# List Messaging Connections
Source: https://docs.revring.ai/api-reference/messaging-connections/list
GET /messaging-connections
Retrieve all messaging provider connections for your organization. Secrets (such as the provider Auth Token) are never returned.
# Update Messaging Connection
Source: https://docs.revring.ai/api-reference/messaging-connections/update
PATCH /messaging-connections/{id}
Update a messaging connection. If credentials change they are re-verified with the provider. Provide authToken only to rotate it.
# Connect Twilio
Source: https://docs.revring.ai/api-reference/sip-trunks/connect-twilio
POST /sip-trunks/connect/twilio
Connects Twilio numbers in one step: creates an Elastic SIP trunk in your Twilio account with origination to sip.revring.ai and a fresh termination credential, moves the chosen numbers onto it, and creates the matching RevRing trunk. With agentId, the numbers are also assigned to that agent for inbound calls (and the trunk becomes its outbound trunk if it has none). If any step fails, everything created in Twilio is removed and your numbers keep their current routing.
# Create SIP Trunk
Source: https://docs.revring.ai/api-reference/sip-trunks/create
POST /sip-trunks
Create a new SIP trunk. Provide phone numbers that will route through this trunk.
# Delete SIP Trunk
Source: https://docs.revring.ai/api-reference/sip-trunks/delete
DELETE /sip-trunks/{id}
Delete a SIP trunk. Cannot delete if agents are using it.
# Get SIP Trunk
Source: https://docs.revring.ai/api-reference/sip-trunks/get
GET /sip-trunks/{id}
Retrieve a specific SIP trunk by ID
# List SIP Trunks
Source: https://docs.revring.ai/api-reference/sip-trunks/list
GET /sip-trunks
Retrieve all SIP trunks for your organization. SIP trunks connect your AI agents to the phone network.
# List Available Phone Numbers
Source: https://docs.revring.ai/api-reference/sip-trunks/list-available-numbers
GET /sip-trunks/{id}/available-numbers
Returns the trunk's phone numbers split into those that are available to assign and those already assigned to agents. Useful when assigning an inbound number to an agent to see what is free without having to track assignments manually.
# List Twilio Numbers
Source: https://docs.revring.ai/api-reference/sip-trunks/list-twilio-numbers
POST /sip-trunks/connect/twilio/numbers
Lists the phone numbers on a Twilio account and where each one routes today, so you can choose which to connect. Numbers already on a RevRing trunk are flagged.
# Update SIP Trunk
Source: https://docs.revring.ai/api-reference/sip-trunks/update
PATCH /sip-trunks/{id}
Update SIP trunk configuration including phone numbers
# Cancel Test Run
Source: https://docs.revring.ai/api-reference/test-suites/cancel-run
POST /test-suites/{id}/runs/{runId}/cancel
Cancel a running or queued test run. Already completed or cancelled runs cannot be cancelled.
# Create Test Suite
Source: https://docs.revring.ai/api-reference/test-suites/create
POST /test-suites
Create a new test suite targeting a specific agent
# Create Test Case
Source: https://docs.revring.ai/api-reference/test-suites/create-test
POST /test-suites/{id}/tests
Add a test case to a test suite. Each test case includes a script for the tester to follow and a scoring rubric for evaluation.
# Create Test From Call
Source: https://docs.revring.ai/api-reference/test-suites/create-test-from-call
POST /test-suites/{id}/tests/from-call
Draft a test case from a real call: the caller's side becomes the script and the call's failed success criteria (or `focus`) the scoring rubric. The call's variables are replayed. Saved to the suite unless `save` is false.
# Delete Test Suite
Source: https://docs.revring.ai/api-reference/test-suites/delete
DELETE /test-suites/{id}
Delete a test suite and all its test cases and runs
# Delete Test Case
Source: https://docs.revring.ai/api-reference/test-suites/delete-test
DELETE /test-suites/{id}/tests/{testId}
Remove a test case from a test suite
# Create Suite From Failing Calls
Source: https://docs.revring.ai/api-reference/test-suites/from-failing-calls
POST /test-suites/from-failing-calls
Build a test suite from an agent's recent calls that failed one evaluation criterion (each call becomes a test, as in Create Test From Call) and start a run.
# Generate Test Cases
Source: https://docs.revring.ai/api-reference/test-suites/generate-tests
POST /test-suites/{id}/generate-tests
Use AI to generate test case suggestions based on a natural language prompt. Generated tests are returned as suggestions and are not automatically added to the suite.
# Get Test Suite
Source: https://docs.revring.ai/api-reference/test-suites/get
GET /test-suites/{id}
Retrieve a test suite by ID
# Get Test Run
Source: https://docs.revring.ai/api-reference/test-suites/get-run
GET /test-suites/{id}/runs/{runId}
Retrieve a specific test run with its results
# Get Suggested Fix for a Run
Source: https://docs.revring.ai/api-reference/test-suites/get-run-suggested-fix
GET /test-suites/{id}/runs/{runId}/suggest-fix
Fetch the run-level suggestion. Poll this every couple of seconds after requesting one: `status` stays `running` while it is being prepared and becomes `ready` when `result` holds the suggestion. `failed` means it could not be prepared; request a new one.
# Get Suggested Fix
Source: https://docs.revring.ai/api-reference/test-suites/get-suggested-fix
GET /test-suites/{id}/attempts/{attemptId}/suggest-fix
Fetch the suggestion for a failed test attempt. Poll this every couple of seconds after requesting one: `status` stays `running` while it is being prepared and becomes `ready` when `result` holds the suggestion. `failed` means it could not be prepared; request a new one.
# Get Test Case
Source: https://docs.revring.ai/api-reference/test-suites/get-test
GET /test-suites/{id}/tests/{testId}
Retrieve a specific test case
# List Test Suites
Source: https://docs.revring.ai/api-reference/test-suites/list
GET /test-suites
List all test suites for your organization with pagination
# List Test Runs
Source: https://docs.revring.ai/api-reference/test-suites/list-runs
GET /test-suites/{id}/runs
List test runs for a test suite with pagination
# List Test Cases
Source: https://docs.revring.ai/api-reference/test-suites/list-tests
GET /test-suites/{id}/tests
List all test cases in a test suite
# Start Test Run
Source: https://docs.revring.ai/api-reference/test-suites/start-run
POST /test-suites/{id}/runs
Start a new test run. This initiates real phone calls between a tester and the target agent for each test case. The suite must have at least one test case.
# Suggest a Fix
Source: https://docs.revring.ai/api-reference/test-suites/suggest-fix
POST /test-suites/{id}/attempts/{attemptId}/suggest-fix
Ask for a prompt change that would make a failed test attempt pass.
Preparing a suggestion takes time, especially for agents with long prompts, so this starts the work and waits up to 105 seconds. If the suggestion is ready in that time it is returned directly; if not, the response is `status: running` and you fetch it with Get Suggested Fix.
Pass `wait=0` to skip the wait and always get `status: running` straight away. This is the recommended pattern, because the request never depends on how long preparation takes.
Asking again while a suggestion is already being prepared joins the one in progress instead of starting another, and a suggestion prepared within the last 10 minutes is returned immediately, so repeating a request after a timeout is instant. Suggestions are available for failed attempts only.
# Suggest a Fix for a Run
Source: https://docs.revring.ai/api-reference/test-suites/suggest-run-fix
POST /test-suites/{id}/runs/{runId}/suggest-fix
Ask for one prompt change that would make every failed test in a run pass.
Requesting a fix per attempt produces separate prompts that do not know about each other, and applying one can undo another. This endpoint analyzes all of the run's failed attempts together and returns a single corrected prompt with a per-attempt breakdown, so failures that share a root cause get one coherent edit.
The wait, polling, and reuse behavior is identical to the per-attempt Suggest a Fix: the request waits up to 105 seconds, `wait=0` returns `status: running` immediately for polling with Get Suggested Fix for a Run, asking again joins a preparation already in progress, and a suggestion prepared within the last 10 minutes is returned instantly.
The run must be finished and must contain at least one failed attempt. On runs with more than 12 failures, the most recent 12 are analyzed and the rest are listed in `result.skippedAttemptIds`.
# Update Test Suite
Source: https://docs.revring.ai/api-reference/test-suites/update
PATCH /test-suites/{id}
Update test suite configuration
# Update Test Case
Source: https://docs.revring.ai/api-reference/test-suites/update-test
PATCH /test-suites/{id}/tests/{testId}
Update a test case's script, rubric, or configuration
# Create Voice
Source: https://docs.revring.ai/api-reference/voices/create
POST /voices
Create a custom voice for your organization from an audio sample. Send `multipart/form-data` with a display name and a clear recording of a single speaker (about 10 to 30 seconds). The new voice can be assigned to any agent via its `voiceId`.
# Delete Voice
Source: https://docs.revring.ai/api-reference/voices/delete
DELETE /voices/{id}
Delete one of your organization's custom voices. Built-in voices cannot be deleted. Any agent still using the voice falls back to a default voice.
# Design Voices
Source: https://docs.revring.ai/api-reference/voices/design
POST /voices/design
Generate candidate voices from a plain-text description. Returns one or more options, each with a short audio preview. Save the one you like with Create Voice.
# Get Voice
Source: https://docs.revring.ai/api-reference/voices/get
GET /voices/{id}
Retrieve a specific voice by ID
# List Voices
Source: https://docs.revring.ai/api-reference/voices/list
GET /voices
Retrieve the voices available to your organization. Use a voice's `id` as an agent's `voiceId` to set how it speaks.
# Preview Voice
Source: https://docs.revring.ai/api-reference/voices/preview
GET /voices/{id}/preview
Get a short spoken preview of a voice as an MP3. Optionally provide your own text.
# Rename Voice
Source: https://docs.revring.ai/api-reference/voices/rename
PATCH /voices/{id}
Rename one of your organization's custom voices. Built-in voices cannot be changed.
# Add Studio Voice
Source: https://docs.revring.ai/api-reference/voices/studio-add
POST /voices/studio
Add a Studio library voice to your organization's voices. The returned voice `id` is used like any other voice (an agent's `voiceId`, or a per-call `voiceId`). Adding the same library voice again returns the existing voice. Calls that use a Studio voice are billed an additional 2 cents per minute.
# Search Studio Library
Source: https://docs.revring.ai/api-reference/voices/studio-library
GET /voices/studio/library
Search the Studio library of premium voices. Results are display-only; add one to your organization with Add Studio Voice. Calls that use a Studio voice are billed an additional 2 cents per minute.
# Preview Studio Voice
Source: https://docs.revring.ai/api-reference/voices/studio-preview
GET /voices/studio/library/{voiceId}/preview
Get a short spoken preview of a Studio library voice as an MP3, before adding it. Optionally provide your own text.
# Inbound Message Webhook
Source: https://docs.revring.ai/api-reference/webhooks/message-received
webhook message-received
Sent when someone texts an agent whose inbound mode is 'notify'. RevRing forwards the incoming message so your system can reply (via the Send Message API). In 'auto_reply' mode RevRing replies automatically and no webhook is sent.
# Post-Call Webhook
Source: https://docs.revring.ai/api-reference/webhooks/post-call
webhook post-call
Called after a call ends with complete call details including transcript, recording, and summary.
# Pre-Call Webhook
Source: https://docs.revring.ai/api-reference/webhooks/pre-call
webhook pre-call
Called before an inbound call is answered. Return variables to be merged with the agent's default variables for this call.
# Disable Webhook Signing
Source: https://docs.revring.ai/api-reference/webhooks/signing-disable
DELETE /webhooks/signing-secret
Removes the secret; webhooks are sent unsigned again.
# Webhook Signing Secret
Source: https://docs.revring.ai/api-reference/webhooks/signing-secret
POST /webhooks/signing-secret
Creates the signing secret, or replaces it. Every webhook RevRing sends your organization (pre-call, post-call, message received) is then signed with `X-RevRing-Signature` (see the Webhooks guide for verification). The full secret is returned only by this call. After a rotation the new secret is live within a minute.
# Get Webhook Signing State
Source: https://docs.revring.ai/api-reference/webhooks/signing-state
GET /webhooks/signing-secret
Whether webhooks sent to your organization are signed, and the last four characters of the live secret.
# What's New
Source: https://docs.revring.ai/changelog
Platform changes that affect how you build with RevRing, newest first.
## October 6, 2026
### Do-not-call requests, handled automatically
Turn on `doNotCallDetection` (or **Detect do-not-call requests** on an agent's Advanced tab): when a caller asks not to be called again, the call shows `doNotCallRequested: true`, the post-call webhook carries it, and their number joins your do-not-call list so campaigns never dial it. Declining an offer or asking for a call back later does not count. [Guide](/platform/campaigns#add-callers-who-ask-automatically)
### A/B test agent versions
Split an inbound number's calls between versions of an agent with `trafficSplit` (2 to 5 environments with weights), then compare them with `GET /v1/analytics/versions`: failure rate, call length, negative sentiment and success-criteria pass rates per version. [Guide](/platform/versioning#split-calls-between-versions)
### Response latency on every call
Each call's `metrics.latency` now shows how quickly the agent answered: the caller's wait from the end of their speech to the agent's reply, broken into turn detection, transcription, thinking and voice, each as p50, p90 and max across the call's turns. [Guide](/platform/webhooks#response-latency)
### One agent, many languages
Pass `language` when you place a call (or from your pre-call webhook for inbound calls) to run that call in the caller's language, and give the agent `languagePresets`: a voice and opening line per language, used whenever a call runs in that language. Agents imported from ElevenLabs bring their per-language opening lines. [Guide](/platform/multilingual#one-agent-many-languages)
### Alerts
Hear about problems before your customers do. Create alerts on failed calls, calls failing a success criterion, negative caller sentiment, tool errors, undelivered post-call webhooks or call volume, for one agent or all, over 15 minutes to 24 hours. Each alert sends one webhook or Slack message when it fires and one when it resolves, with a test button and a history of every notification. Open **Alerts** in the dashboard or use `/v1/alert-rules`. [Guide](/platform/alerts)
### Knowledge from whole websites, kept current
A URL source can now read a whole help center or documentation site: add `crawl` with `maxPages` (up to 50) and an optional `includePath`, and RevRing follows the site's links under that path into one source. Set `refreshDays` to read the pages again on a schedule, or refresh a source on demand with `POST /v1/knowledge-bases/{id}/sources/{sourceId}/refresh`. Agents keep answering from the current content during a refresh, and new content replaces it only once it is fully indexed. [Guide](/platform/knowledge-bases#whole-websites)
### Caller sentiment, re-run analysis and call export
Every call with caller speech now carries `sentiment` (positive, neutral, negative or unknown) on the call and in the post-call webhook; Retell-format webhooks fill `user_sentiment` from it. After changing an agent's extraction fields or success criteria, re-run the analysis on past calls (`POST /v1/calls/{id}/reanalyze`, up to 50 at once with `POST /v1/calls/reanalyze`, or **Re-run analysis** on a call). Download call history as CSV with sentiment, variables, extracted data and evaluation results (`GET /v1/calls/export`, or **Export CSV** on Call Logs). [Guide](/platform/webhooks#caller-sentiment)
### Transfer events on your webhook
Add `transfer.started`, `transfer.bridged` and `transfer.cancelled` to an agent's `webhookEvents` (or turn on **Also send transfer events**) to hear about transfers as they happen: started (warm or cold, with the destination), connected, or not completed (declined, voicemail or failed). Agents using the Retell compatibility format receive Retell's `transfer_started`, `transfer_bridged` and `transfer_cancelled`. [Guide](/platform/webhooks#transfer-events)
### Listen to calls live
Open any call in progress in the dashboard and select **Listen** to hear both sides as it happens. Nobody on the call can hear you or knows you are listening, so the call is never affected. The transcript on the same page updates every few seconds while the call runs.
### Remove personal details from call transcripts
Set an agent's `storageMode` to `redact_pii` (or choose it under **Advanced > Call data storage**) and names, phone numbers, emails, addresses, dates of birth, ID, card, account and medical record numbers, and passwords are replaced with labels like `[phone_number]` in every call's transcript and summary, before the call is stored or sent to your webhook. Pick categories with `piiCategories`. Calls show `piiRedacted: true`. Agents imported from Retell AI or ElevenLabs carry their PII settings over. [Guide](/platform/data-controls)
## October 5, 2026
### Connect Twilio in one step
Bring Twilio numbers over with your Account SID and Auth Token: RevRing creates the Elastic SIP trunk, points it here, moves the numbers you pick and can assign them to an agent. From the dashboard (**SIP Trunks > Connect Twilio**) or `POST /v1/sip-trunks/connect/twilio`. [Guide](/platform/telephony#connect-twilio-in-one-step)
### Fix failing calls
Build a test suite from an agent's recent calls that failed a success criterion and run it, in one step from the dashboard Overview or `POST /v1/test-suites/from-failing-calls`. Success criteria now count a skipped required action as `fail`; an outcome the call never reached stays `unknown`. [Guide](/platform/test-suites#fix-failing-calls)
### Create a test from a real call
One click on a call (or `POST /v1/test-suites/{id}/tests/from-call`) turns it into a test case: the caller's side becomes the script, the failed success criteria the rubric. [Guide](/platform/test-suites#from-a-real-call)
### Call started event
Set `webhookEvents: ["call.started"]` on an agent to get a webhook when each call connects, in RevRing's shape or Retell's `call_started`. [Guide](/platform/webhooks#call-started-event)
### Route inbound calls from your pre-call webhook
The pre-call webhook's answer can now pick the agent or environment for the call, override the opening line, voice or prompt, or reject the call. Retell and ElevenLabs inbound webhook answers work unchanged. [Guide](/platform/webhooks#route-personalize-or-reject-the-call)
### Manage RevRing from Claude, Cursor and VS Code
A management MCP server at `https://api.revring.ai/v1/mcp` lets AI tools list and edit agents, place and end calls, and read transcripts and results with your API key. [Guide](/mcp-server)
### Keep your webhook handler when you switch
Set `postCallWebhookFormat` to `retell` or `elevenlabs` and the post-call webhook arrives in that platform's payload shape, filled from the RevRing call record. The importer sets it automatically. [Guide](/platform/webhooks#compatibility-formats)
### Knowledge bases hold 50 sources
The limit per knowledge base is now 50 sources (was 10).
### Campaigns
Call a contact list with one agent. Contacts without a time zone are called in the local time of their number's area code, and `voicemailStrategy: "last_attempt"` leaves a voicemail only on the final attempt. Calling windows follow each contact's own time zone, retries happen automatically by outcome (no answer, busy, voicemail, declined, failed), new calls are paced to suit your carrier, numbers on your do-not-call list are never dialed, and a CSV export carries every contact's outcome with extraction and evaluation columns. Campaign calls carry `campaignId` on the call record and in the post-call webhook. [Guide](/platform/campaigns)
### Import agents from Retell AI and ElevenLabs
Bring existing voice agents over with your API key for the other platform. A dry run reports how every setting carries over (prompt, opening, variables, tools, transfers, voicemail, knowledge, post-call analysis) and what is left to you; importing creates the agents with their tools and knowledge bases, and importing again updates them. [Guide](/platform/importing-agents)
### End a live call from your server
`POST /v1/calls/{id}/end` hangs up a call in progress immediately; it is recorded as `API_ENDED` and still gets its recording, summary and post-call webhook. Use it when your own logic decides the call should stop. [Reference](/api-reference/calls/end)
### Concurrency headroom
`GET /v1/analytics/active` now also returns `concurrency` (`active`, `limit`, `available`), counted exactly like the check behind `concurrency_limit`, so dialers can pace calls instead of retrying. [Reference](/api-reference/analytics/active)
### Spoken text never reads a placeholder aloud
If an opening line, voicemail message, hold or intro message references a `{{variable}}` that has no value on a call, the placeholder is dropped and the sentence tidied ("Hi , this is Maya" becomes "Hi, this is Maya") instead of being read out.
### Webhook signing
Every webhook RevRing sends (pre-call, post-call, message received) can now be signed. Create a secret with `POST /v1/webhooks/signing-secret` and verify the `X-RevRing-Signature` header on the raw body. Opt-in per organization; nothing changes until you create a secret. [Guide](/platform/webhooks#verify-webhook-sources)
### Webhook retries
A post-call webhook that fails (connection error, timeout, non-2xx) is retried 1, 5 and 30 minutes later. Handlers should be idempotent on the call `id`. The outcome is on the call record as `postCallWebhookStatus` and `postCallWebhookAttempts`. [Guide](/platform/webhooks#response-expectations)
### Test runs against a live version
Test runs use the saved draft by default. Pass `environment` (`production` or an environment name) on `POST /test-suites/{id}/runs` to test the version callers get. [Guide](/platform/versioning#testing-before-you-publish)
## October 4, 2026
### Drafts, publishing and environments
Agents can now be edited without going live. Switch an agent to manual publishing (`publishMode: "manual"`), edit the draft, test it, and publish when ready; earlier versions can be made live in one call. Named environments pin a version with their own variable defaults and can be bound to inbound numbers, the web widget and `POST /calls`. Every call record carries the version it ran on. [Guide](/platform/versioning)
### Post-call data extraction
Define typed fields per agent (`postCallExtraction`) and get them filled from each conversation on the call record and in the post-call webhook as `extraction`. [Guide](/platform/webhooks#post-call-data-extraction)
### Post-call evaluation
Write success criteria per agent (`postCallEvaluation`) and every call is judged pass, fail or unknown with a one-sentence reason. Pass rates per criterion are on the analytics overview and the dashboard. [Guide](/platform/webhooks#post-call-evaluation)
### SIP trunk address validation
A trunk's proxy must now be a hostname or IP that resolves. Typos such as a misspelled carrier domain are refused when the trunk is created instead of producing a trunk that can never place a call. [Reference](/api-reference/sip-trunks/create)
### Noise suppression for every agent
Background noise and other voices on the caller's side are suppressed before the agent listens. On by default for all agents; turn it off per agent with `noiseCancellation: "off"`.
# Introduction
Source: https://docs.revring.ai/index
Welcome to the RevRing AI documentation website
## Setting up
Get your first RevRing agent set up and running in minutes.
Follow our quickstart guide to handle your first AI phone call
## Platform
Guides for telephony setup, inbound and outbound calling, tools, and webhooks.
Connect SIP trunks from Twilio, Telnyx, or any telephony provider.
Define and configure tools for agents to use during calls.
Handle inbound calls on any of your phone numbers.
Send outbound calls through our dashboard or API.
## API Reference
View our full API reference and copy examples to build on top of the RevRing API.
View all available RevRing API endpoints and how to use them.
## Need help?
Contact us anytime through Slack or email!
# MCP Servers
Source: https://docs.revring.ai/mcp-server
Manage agents and calls, and search RevRing documentation, from AI tools like Claude, Cursor and VS Code
RevRing hosts two MCP (Model Context Protocol) servers:
* **Management server** at `https://api.revring.ai/v1/mcp`: your AI tool works with your RevRing account. It can list and edit agents, place and end calls, and read transcripts and results.
* **Documentation server** at `https://docs.revring.ai/mcp`: your AI tool searches the RevRing docs.
## Management server
```
https://api.revring.ai/v1/mcp
```
Authenticate with an API key from **Developer > API Keys**, sent as a bearer token (`Authorization: Bearer rr_...`) or as `x-api-key`. Everything the tools do is scoped to that key's organization and runs through the same validation as the REST API.
| Tool | What it does |
| - | - |
| `list_agents` | List agents, optionally filtered by name |
| `get_agent` | Full configuration of one agent |
| `create_agent` | Create an agent (same fields as [Create Agent](/api-reference/agents/create)) |
| `update_agent` | Change an agent's settings (same fields as [Update Agent](/api-reference/agents/update)) |
| `list_calls` | Recent calls, filtered by agent, status, direction, numbers or dates |
| `get_call` | One call with transcript, summary, extraction, evaluation and recording |
| `place_call` | Call a number with an agent |
| `end_call` | Hang up a call in progress |
| `list_voices` | Voices available to your organization |
Read-only tools are marked as such, so clients can ask before running anything that changes your account or places a call.
**Claude Code**
```bash theme={null}
claude mcp add --transport http revring https://api.revring.ai/v1/mcp --header "Authorization: Bearer $REVRING_API_KEY"
```
**Cursor** (`~/.cursor/mcp.json`) and **VS Code** (`.vscode/mcp.json`, under `servers` with `"type": "http"`):
```json theme={null}
{
"mcpServers": {
"revring": {
"url": "https://api.revring.ai/v1/mcp",
"headers": { "Authorization": "Bearer rr_your_api_key" }
}
}
}
```
Then ask, for example: *"List my agents and show me the last five failed calls for the support agent"* or *"Raise the support agent's turn timeout to 10 seconds."*
An API key gives full access to your organization. Use a dedicated key for AI tools so you can revoke it on its own.
## Documentation server
RevRing hosts an MCP (Model Context Protocol) server that lets AI tools search our documentation directly. This means tools like Cursor, VS Code Copilot, Claude, and Claude Code can pull accurate, up-to-date information from RevRing docs while helping you build integrations.
```
https://docs.revring.ai/mcp
```
Whether you're writing API integration code, configuring agents programmatically, or building custom workflows — connecting this MCP server gives your AI assistant direct access to every guide, API reference, and code example in our documentation.
## Connect to Cursor
Click the button above to install the RevRing MCP server in Cursor. Or configure it manually:
Add the following to `.cursor/mcp.json` in your project directory (or `~/.cursor/mcp.json` for global access):
```json theme={null}
{
"mcpServers": {
"RevRing": {
"url": "https://docs.revring.ai/mcp"
}
}
}
```
To verify it's working, ask Cursor a question about RevRing — for example, *"How do I send an outbound call with the RevRing API?"*
## Connect to VS Code
[Install in VS Code](https://vscode.dev/redirect/mcp/install?name=RevRing\&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fdocs.revring.ai%2Fmcp%22%7D) — click to add the RevRing MCP server automatically. Or configure it manually:
Create a `.vscode/mcp.json` file in your project root:
```json theme={null}
{
"servers": {
"RevRing": {
"type": "http",
"url": "https://docs.revring.ai/mcp"
}
}
}
```
Open GitHub Copilot chat and ask a question about RevRing. VS Code may prompt you to trust the MCP server on first use — select **Allow** to enable it.
## Connect to Claude
Navigate to the [Connectors](https://claude.ai/settings/connectors) page in your Claude settings.
Select **Add custom connector** and enter:
* **Name**: `RevRing`
* **URL**: `https://docs.revring.ai/mcp`
When chatting with Claude, select the attachments button (the **+** icon) and choose the RevRing connector. Claude can now search RevRing docs to answer your questions.
## Connect to Claude Code
Run the following command in your terminal:
```bash theme={null}
claude mcp add --transport http RevRing https://docs.revring.ai/mcp
```
Verify the connection:
```bash theme={null}
claude mcp list
```
## Connect to ChatGPT
In ChatGPT, navigate to your Connectors settings and create a new connector with the URL:
```
https://docs.revring.ai/mcp
```
Start a new chat, select the **+** menu, choose **More**, and select the RevRing connector.
## What's available
The MCP server exposes a **search tool** that AI applications use to query all indexed RevRing documentation. This covers:
* **Platform guides** — telephony setup, inbound/outbound calls, agent tools, knowledge bases, webhooks, and more
* **API reference** — every endpoint, request/response schema, and error code
* **Code examples** — integration patterns and configuration samples
Most AI clients will call the search tool automatically when your question is relevant to RevRing. You can also explicitly ask your AI tool to search RevRing docs.
## Use cases
* **Building API integrations** — ask your AI tool to write code that sends calls, manages agents, or processes webhooks, and it will pull the correct endpoints and schemas from RevRing docs
* **Configuring agents** — get accurate instructions for setting up tools, knowledge bases, conversation flows, and advanced settings
* **Debugging** — describe an issue and your AI tool can search for relevant error codes, troubleshooting steps, and configuration requirements
* **Learning the platform** — explore RevRing capabilities through natural conversation with any connected AI tool
# Alerts
Source: https://docs.revring.ai/platform/alerts
Get a webhook or Slack message when failed calls, tool errors, negative sentiment or call volume cross a threshold.
Alerts watch your calls and tell you when something needs attention: more calls failing than usual, a tool your agent depends on returning errors, callers getting frustrated, or call volume dropping to nothing. Each alert sends one message when it fires and one when it resolves.
## Create an alert
**Dashboard:** open **Alerts** in the sidebar and click **New alert**. Pick what to watch, which agent's calls (or all agents), the threshold and the time window, and where to send it.
**API:**
```bash theme={null}
curl -X POST https://api.revring.ai/v1/alert-rules \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Failed calls on the booking line",
"agentId": "AGENT_ID",
"metric": "call_failure_rate",
"comparator": "above",
"threshold": 20,
"windowMinutes": 60,
"minCalls": 10,
"webhookUrl": "https://example.com/revring-alerts",
"slackWebhookUrl": "https://hooks.slack.com/services/T000/B000/XXXX"
}'
```
| Field | Description |
| - | - |
| `name` | Shown in notifications and the dashboard. |
| `metric` | What to watch (see below). |
| `agentId` | Only this agent's calls. Leave it out for all agents. |
| `comparator` | `above` (default) or `below`. |
| `threshold` | A percent from 0 to 100 for rates, or a number of calls for `call_count`. |
| `windowMinutes` | The period looked at: `15`, `60` (default), `240` or `1440`. |
| `minCalls` | Rates are only judged with at least this many calls in the period (default 10), so one bad call in a quiet hour does not fire an alert. |
| `webhookUrl` | An https endpoint for a JSON notification. |
| `slackWebhookUrl` | A Slack [incoming webhook](https://api.slack.com/messaging/webhooks) URL. |
| `enabled` | Turn the alert off without deleting it. |
Each alert needs a webhook, a Slack webhook, or both. An organization can have up to 20 alerts.
## What you can watch
| Metric | Value |
| - | - |
| `call_failure_rate` | Percent of finished calls that failed. Outbound calls that were not answered count as failed. |
| `evaluation_fail_rate` | Percent of calls where at least one [success criterion](/platform/webhooks#post-call-evaluation) was judged `fail` (calls with no `pass` or `fail` verdict are not counted). |
| `negative_sentiment_rate` | Percent of calls with negative [caller sentiment](/platform/webhooks#caller-sentiment). |
| `tool_error_rate` | Percent of tool calls (custom tools and webhooks) that returned an error or timed out. |
| `webhook_failure_rate` | Percent of post-call webhooks that could not be delivered after all retries. |
| `call_count` | Number of calls. Use `below` to hear when calls stop coming in. |
## How alerts are checked
Every alert is checked every 5 minutes over its time window. When the value crosses the threshold the alert **fires** and sends one notification. It sends nothing more while it stays over the threshold, then sends one more when it **resolves**. Changing an alert's condition takes effect at the next check: an alert that no longer meets its new condition resolves.
To see an alert's current value without sending anything, call `GET /v1/alert-rules/{id}/value`. To check your endpoints, `POST /v1/alert-rules/{id}/test` (or the send icon in the dashboard) sends a test notification. `GET /v1/alert-rules/{id}/events` lists past notifications with the delivery result for each channel.
## Webhook payload
```json theme={null}
{
"event": "alert.triggered",
"alert": {
"id": "cmuw...",
"name": "Failed calls on the booking line",
"metric": "call_failure_rate",
"comparator": "above",
"threshold": 20,
"windowMinutes": 60,
"agentId": "AGENT_ID"
},
"value": 27.5,
"sample": 40,
"summary": "Failed calls: 27.5% over the last 1 hour (40 in the sample); alert when above 20%.",
"timestamp": "2026-10-06T14:05:00.000Z"
}
```
`event` is `alert.triggered`, `alert.resolved` or `alert.test`. `sample` is the number of calls (or tool calls, or webhooks) the value was computed from. Alert webhooks are signed like your other webhooks when you have a [signing secret](/platform/webhooks#verify-webhook-sources).
Slack messages carry the alert name and the same summary line.
# Campaigns
Source: https://docs.revring.ai/platform/campaigns
Call a contact list with one agent: calling windows in each contact local time, automatic retries by outcome, pacing, a do-not-call list and a CSV of results.
A campaign calls a list of contacts with one agent. RevRing dials inside the calling window you set, in each contact's own time zone when you provide one, retries the outcomes you choose (no answer, busy, voicemail, declined, failed), paces new calls so your carrier is not overwhelmed, never dials numbers on your do-not-call list, and gives you a CSV of every contact with the result of their last call.
## Create and start a campaign
```bash theme={null}
curl -X POST https://api.revring.ai/v1/campaigns \
-H "x-api-key: $REVRING_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "October renewals",
"agentId": "cmagent123",
"fromNumber": "+14155550100",
"callingWindow": { "start": "09:00", "end": "18:00", "days": [1, 2, 3, 4, 5], "timezone": "America/New_York" },
"retryPolicy": { "maxAttempts": 3, "retryDelayMinutes": 120, "retryOn": ["no_answer", "busy"] },
"maxConcurrency": 5,
"callsPerSecond": 1,
"contacts": [
{ "toNumber": "+14155550123", "variables": { "first_name": "Ana", "renewal_date": "Oct 31" } },
{ "toNumber": "+13125550188", "variables": { "first_name": "Sam" }, "timezone": "America/Chicago" }
],
"start": true
}'
```
Each contact's `variables` are passed to the call exactly like `variables` on [Send Call](/api-reference/calls/send), merged over the campaign's `defaultVariables`. Use them in the prompt and opening line as `{{first_name}}`.
Without `"start": true` the campaign is created as a draft; start it with `POST /campaigns/{id}/start`. Set `startAt` to hold dialing until a later time.
| Setting | Meaning | Default |
| - | - | - |
| `fromNumber` | Caller ID. Must be a number on the agent's SIP trunk | Required |
| `callingWindow` | `start` and `end` (24h `HH:MM`), `days` (0 = Sunday), and `timezone`. Each contact is called in its own local time: its `timezone` when you give one, otherwise the zone of its number's area code, otherwise the window's `timezone` | Any time |
| `retryPolicy` | `maxAttempts` (1 to 10), `retryDelayMinutes`, and which outcomes to retry | One attempt, no retries |
| `maxConcurrency` | Most calls this campaign runs at once | Your plan's concurrency |
| `callsPerSecond` | New calls started per second (0.1 to 10). Many carriers allow 1 per second per trunk | 1 |
| `voicemailStrategy` | `agent`: the agent's voicemail setting on every attempt. `last_attempt`: earlier attempts that reach voicemail hang up and are retried; the message is left only on the final attempt | `agent` |
| `environment` | [Environment](/platform/versioning) the agent runs on | Production |
| `startAt` | Don't dial before this time | Immediately |
Campaign calls share your organization's concurrency with everything else. Calls you place with [Send Call](/api-reference/calls/send) that are waiting in the queue go first.
## Outcomes and retries
When a call ends, the contact gets an `outcome`:
| Outcome | When |
| - | - |
| `answered` | Someone picked up and the conversation happened (including transferred calls) |
| `voicemail` | Voicemail was detected; the agent's voicemail setting applied |
| `no_answer` | It rang out, or nobody spoke |
| `busy` | The line was busy |
| `declined` | The call was rejected |
| `failed` | The call could not be placed |
| `do_not_call` | The number is on your do-not-call list; it was not dialed |
With `voicemailStrategy: "last_attempt"`, voicemail is always retried until the last attempt, which leaves the agent's voicemail message once.
If the outcome is in `retryOn` and the contact has attempts left, it waits `retryDelayMinutes` and is called again, still only inside the calling window. Otherwise the contact is `completed` (answered, voicemail, or an outcome you don't retry) or `exhausted` (retries used up).
## Follow progress
`GET /campaigns/{id}` returns `progress`: the number of contacts in each status and each outcome. `GET /campaigns/{id}/contacts` lists contacts with `status`, `outcome`, `attempts`, `nextAttemptAt` and `lastCallId`; filter with `status` or `outcome`.
Every call a campaign places carries `campaignId` on the call record and in the [post-call webhook](/platform/webhooks), so your existing handler can tell campaign calls apart.
The campaign completes on its own when every contact is settled.
## Export results
`GET /campaigns/{id}/export` returns a CSV with one row per contact: number, status, outcome, attempts, the last call's ID, status, duration, summary and recording, every contact variable (`var.*`), every [extraction](/platform/webhooks#post-call-data-extraction) field (`extraction.*`) and every [evaluation](/platform/webhooks#post-call-evaluation) result (`evaluation.*`).
## Pause, resume, cancel
* `POST /campaigns/{id}/pause` stops new dials. Calls in progress finish and their outcomes are recorded.
* `POST /campaigns/{id}/start` resumes.
* `POST /campaigns/{id}/cancel` stops the campaign for good. Contacts not yet called become `canceled`.
Contacts can be added to a draft, running or paused campaign with `POST /campaigns/{id}/contacts` (up to 10,000 per request; numbers already in the campaign are skipped).
## Do-not-call list
Numbers on your organization's do-not-call list are never dialed by a campaign. They are marked `skipped` with outcome `do_not_call`.
```bash theme={null}
curl -X POST https://api.revring.ai/v1/do-not-call \
-H "x-api-key: $REVRING_API_KEY" -H "Content-Type: application/json" \
-d '{ "numbers": ["+14155550123"], "reason": "asked not to be called" }'
```
List entries with `GET /do-not-call` and remove one with `DELETE /do-not-call/{e164}`.
### Add callers who ask automatically
Turn on `doNotCallDetection` on an agent (or **Detect do-not-call requests** on its Advanced tab) and, after each call, RevRing checks whether the caller asked not to be called again: to stop calling, to be taken off the list, or that they are on a do-not-call list. Declining an offer or asking to be called back later does not count. When they asked, the call shows `doNotCallRequested: true` (also in the post-call webhook) and their number is added to your do-not-call list with the reason `asked on call {id}`, so campaigns never dial it again. Agents imported from Retell AI with do-not-call detection on keep it.
# Conversation Flows
Source: https://docs.revring.ai/platform/conversation-flows
Build structured conversation flows with the RevRing flow JSON format
Conversation flows let you define structured, multi-step call scripts for your AI agents. Instead of relying on a single prompt, you can build a directed graph of nodes (steps) and edges (transitions) that guide the conversation through a precise sequence.
Flows are defined as a JSON format that we call the **flow definition**. You can build flows visually in the RevRing dashboard, or generate them programmatically — including with AI tools like ChatGPT — and import them via the API.
## Overview
A flow consists of:
* **Begin** — which node starts the conversation and who speaks first
* **Nodes** — individual steps (conversation, tool call, logic branch, transfer, etc.)
* **Edges** — connections between nodes that define transitions and conditions
```json theme={null}
{
"schemaVersion": 1,
"begin": { "startNodeId": "node_1", "whoSpeaksFirst": "agent" },
"nodes": [ ... ],
"edges": [ ... ]
}
```
## Flow Definition Reference
### Top-Level Structure
| Field | Type | Required | Description |
| - | - | - | - |
| `schemaVersion` | `1` | Yes | Must be `1` |
| `begin` | object | Yes | Flow entry point configuration |
| `nodes` | array | Yes | List of flow nodes (at least one) |
| `edges` | array | Yes | List of edges connecting nodes |
| `ui` | object | No | Visual editor state (viewport position/zoom) |
### Begin
| Field | Type | Required | Description |
| - | - | - | - |
| `startNodeId` | string | Yes | ID of the first node to execute. Must match a node in `nodes`. |
| `whoSpeaksFirst` | `"agent"` or `"user"` | Yes | Whether the agent speaks first or waits for the caller |
## Node Types
Every node has the following base fields:
| Field | Type | Required | Description |
| - | - | - | - |
| `id` | string | Yes | Unique identifier for the node |
| `type` | string | Yes | One of the node types below |
| `name` | string | Yes | Human-readable label |
| `isGlobal` | boolean | No | If `true`, this node can be triggered from any point in the flow via a global edge |
| `position` | `{ x, y }` | Yes | Visual position in the editor (use any values when generating programmatically) |
| `data` | object | Yes | Type-specific configuration (see below) |
### Conversation
The core node type. The agent speaks and/or listens based on an instruction.
**Type**: `"conversation"`
| Field | Type | Required | Description |
| - | - | - | - |
| `instructionType` | `"prompt"` or `"static"` | Yes | `"prompt"` = LLM interprets the instruction as context. `"static"` = agent speaks the text verbatim. |
| `instruction` | string | Yes | The instruction text or static message. Supports `{{variables}}`. |
| `skipResponse` | boolean | No | If `true`, the agent speaks the instruction and immediately moves to the next node without waiting for a user reply. Requires a `skip` edge. |
| `blockInterruptions` | boolean | No | If `true`, the agent cannot be interrupted while speaking |
**Example:**
```json theme={null}
{
"id": "greeting",
"type": "conversation",
"name": "Greeting",
"position": { "x": 300, "y": 200 },
"data": {
"instructionType": "prompt",
"instruction": "Greet the caller warmly. Introduce yourself as {{agent_name}} from {{company_name}}. Ask how you can help them today."
}
}
```
### Function (Tool Call)
Invokes one of your agent's custom tools during the flow.
**Type**: `"function"`
| Field | Type | Required | Description |
| - | - | - | - |
| `toolName` | string | Yes | Name of the custom tool to invoke (must be configured on the agent) |
| `speakDuringExecution` | boolean | No | If `true`, agent speaks while the tool runs |
| `speakInstruction` | string | No | What to say while the tool executes |
| `speakInstructionType` | `"prompt"` or `"static"` | No | How to interpret the speak instruction |
| `blockInterruptions` | boolean | No | Block interruptions during execution |
| `waitForResult` | boolean | No | Default `true`. Set `false` for fire-and-forget calls. |
| `outputVariables` | array | No | Map tool output keys to flow variables for use in downstream conditions |
Each entry in `outputVariables`:
| Field | Type | Description |
| - | - | - |
| `outputKey` | string | Key in the tool's JSON response |
| `variableName` | string | Flow variable name to store the value as |
**Example:**
```json theme={null}
{
"id": "lookup_order",
"type": "function",
"name": "Look Up Order",
"position": { "x": 300, "y": 400 },
"data": {
"toolName": "lookup_order",
"speakDuringExecution": true,
"speakInstruction": "Let me look that up for you, one moment.",
"speakInstructionType": "static",
"outputVariables": [
{ "outputKey": "status", "variableName": "order_status" },
{ "outputKey": "eta", "variableName": "delivery_eta" }
]
}
}
```
### Logic Split
A branching node with no data of its own — all logic is defined by its outgoing edges (conditions and an else fallback).
**Type**: `"logic_split"`
| Field | Type | Required | Description |
| - | - | - | - |
| (none) | — | — | Data is an empty object `{}` |
The logic split node must have:
* One or more `condition` edges (evaluated in order)
* Exactly one `else` edge (fallback if no conditions match)
**Example:**
```json theme={null}
{
"id": "check_status",
"type": "logic_split",
"name": "Check Order Status",
"position": { "x": 300, "y": 600 },
"data": {}
}
```
### Call Transfer
Transfers the call to another phone number.
**Type**: `"call_transfer"`
| Field | Type | Required | Description |
| - | - | - | - |
| `transferTo` | string | Yes | Destination phone number in E.164 format (e.g. `"+14155551234"`) or a `{{variable}}` |
| `transferMode` | `"cold"` or `"warm"` | No | Cold = immediate transfer. Warm = agent briefs the recipient first. |
| `extension` | string | No | Touch-tone digits dialed after the call connects, to reach an extension behind a phone menu. Allowed: `0-9`, `*`, `#`, and `w` for a 0.5-second pause. Start with `wwww` (2 seconds) so the destination is ready for input, e.g. `"wwww1234"`. Warm transfers only. |
| `speakDuringExecution` | boolean | No | Speak while the transfer is being set up |
| `speakInstruction` | string | No | What to say during transfer |
| `holdMessage` | string | No | Message spoken to the caller while on hold (warm transfer, max 500 chars) |
| `holdMusicEnabled` | boolean | No | Play hold music (warm transfer) |
| `summaryPrompt` | string | No | Prompt used to generate a brief for the recipient (warm transfer, max 2000 chars) |
| `introMessage` | string | No | Message spoken to the recipient before connecting (warm transfer, max 500 chars) |
| `continueRecordingAfterTransfer` | boolean | No | Keep recording after the handoff, so the recording also includes the recipient and caller conversation (warm transfer) |
**Example:**
```json theme={null}
{
"id": "transfer_support",
"type": "call_transfer",
"name": "Transfer to Support",
"position": { "x": 600, "y": 800 },
"data": {
"transferTo": "+12025559999",
"transferMode": "warm",
"speakDuringExecution": true,
"speakInstruction": "Let me connect you with our support team.",
"holdMessage": "Please hold while I connect you.",
"holdMusicEnabled": true,
"continueRecordingAfterTransfer": false
}
}
```
### End Call
Terminates the call, optionally speaking a closing message.
**Type**: `"end"`
| Field | Type | Required | Description |
| - | - | - | - |
| `message` | string | No | Closing message to speak before hanging up |
**Example:**
```json theme={null}
{
"id": "goodbye",
"type": "end",
"name": "End Call",
"position": { "x": 300, "y": 1000 },
"data": {
"message": "Thank you for calling. Have a great day!"
}
}
```
### Press Digit (DTMF)
Navigates an automated phone menu on the other end of the call. The agent stays silent, listens to the menu, and presses the digit your instruction calls for. Use it when your agent calls a business that answers with "press 1 for...".
**Type**: `"press_digit"`
| Field | Type | Required | Description |
| - | - | - | - |
| `instruction` | string | Yes | Which option to choose, in plain language |
| `detectionDelaySeconds` | number | No | How long to let the menu finish before choosing (0 to 10 seconds, default 1) |
**Example:**
```json theme={null}
{
"id": "menu",
"type": "press_digit",
"name": "Reach billing",
"position": { "x": 300, "y": 200 },
"data": {
"instruction": "Choose the option for the billing department. If the menu offers an operator, choose that instead.",
"detectionDelaySeconds": 3
}
}
```
Each time the menu speaks, the agent decides whether to press a digit; the node's transitions then move the flow on as usual.
### Extract Variable
Uses the LLM to extract structured data from the conversation and store it in flow variables for downstream use.
**Type**: `"extract_variable"`
| Field | Type | Required | Description |
| - | - | - | - |
| `variables` | array | Yes | List of variables to extract (at least one) |
Each entry in `variables`:
| Field | Type | Required | Description |
| - | - | - | - |
| `variableName` | string | Yes | Name of the flow variable to store the extracted value |
| `description` | string | Yes | Description of what to extract (used as context for the LLM) |
| `variableType` | `"text"`, `"number"`, `"enum"`, `"boolean"` | Yes | Data type of the variable |
| `enumOptions` | string\[] | No | Required when `variableType` is `"enum"` — the allowed values |
**Example:**
```json theme={null}
{
"id": "get_intent",
"type": "extract_variable",
"name": "Detect Intent",
"position": { "x": 300, "y": 400 },
"data": {
"variables": [
{
"variableName": "caller_intent",
"description": "The primary reason the caller is calling",
"variableType": "enum",
"enumOptions": ["billing", "support", "sales", "other"]
},
{
"variableName": "caller_name",
"description": "The caller's first name if mentioned",
"variableType": "text"
}
]
}
}
```
## Edges
Edges connect nodes and define how the flow transitions between steps.
Condition edges can be attached to **any node type**, not just logic split nodes. For example, you can put condition edges directly on a conversation node to branch based on the caller's response — no logic split needed.
### Edge Fields
| Field | Type | Required | Description |
| - | - | - | - |
| `id` | string | Yes | Unique identifier for the edge |
| `source` | string | Yes | ID of the source node, or `"__global__"` for global edges |
| `target` | string | Yes | ID of the target node |
| `kind` | string | Yes | One of: `"default"`, `"condition"`, `"else"`, `"skip"` |
| `order` | integer | Condition edges only | Evaluation order (0-based). Lower numbers are evaluated first. |
| `condition` | object | Condition edges only | The condition to evaluate |
### Edge Kinds
| Kind | Description |
| - | - |
| `default` | Unconditional transition — used when a node completes normally |
| `condition` | Evaluated in order; first matching condition wins |
| `else` | Fallback when no condition edges match (required on `logic_split` nodes) |
| `skip` | Used with `skipResponse` conversation nodes — immediate transition without waiting for user input |
### Conditions on Any Node
You can attach condition edges to any node type. This is particularly useful on conversation nodes — branch directly based on the caller's response without needing a separate logic split node.
For example, a greeting node that routes callers to different paths:
```json theme={null}
[
{
"id": "e1",
"source": "greeting",
"target": "collect_info",
"kind": "condition",
"order": 0,
"condition": {
"type": "prompt",
"promptText": "The caller wants help and didn't mention billing or tech support"
}
},
{
"id": "e2",
"source": "greeting",
"target": "billing_confirm",
"kind": "condition",
"order": 1,
"condition": {
"type": "prompt",
"promptText": "The caller needs billing help"
}
},
{
"id": "e3",
"source": "greeting",
"target": "tech_confirm",
"kind": "condition",
"order": 2,
"condition": {
"type": "prompt",
"promptText": "The caller needs technical help"
}
}
]
```
If no condition matches, the agent stays at the greeting node and continues the conversation until a condition is met.
### Conditions
Conditions determine whether a `condition` edge is followed. There are two types:
#### Prompt Conditions
The LLM evaluates a natural language question against the conversation context.
```json theme={null}
{
"type": "prompt",
"promptText": "Did the caller agree to schedule an appointment?"
}
```
| Field | Type | Description |
| - | - | - |
| `type` | `"prompt"` | Prompt-based condition |
| `promptText` | string | A yes/no question the LLM evaluates against the conversation |
#### Equation Conditions
Variable-based conditions that compare flow variables against values. No LLM call required.
```json theme={null}
{
"type": "equation",
"match": "all",
"equations": [
{ "variable": "order_status", "operator": "==", "value": "shipped" },
{ "variable": "delivery_eta", "operator": "exists" }
]
}
```
| Field | Type | Description |
| - | - | - |
| `type` | `"equation"` | Equation-based condition |
| `match` | `"any"` or `"all"` | Whether any or all equations must be true. Optional — defaults to `"all"`. |
| `equations` | array | List of equations to evaluate |
Each equation:
| Field | Type | Description |
| - | - | - |
| `variable` | string | Flow variable name to compare |
| `operator` | string | Comparison operator |
| `value` | string | Value to compare against (not required for `exists`/`not_exists`) |
**Available operators:** `==`, `!=`, `contains`, `not_contains`, `contained_in`, `not_contained_in`, `>`, `<`, `>=`, `<=`, `exists`, `not_exists`
## Global Nodes and Edges
Global nodes can be triggered from **any point** in the conversation, not just from a specific predecessor node. This is useful for handling requests that can happen at any time, such as "transfer me to a human" or "I want to cancel."
To make a node global:
1. Set `isGlobal: true` on the node
2. Add one or more edges with `source: "__global__"` targeting that node
3. Global edges must have a `condition` — typically a prompt condition
```json theme={null}
{
"id": "global_transfer_edge",
"source": "__global__",
"target": "transfer_human",
"kind": "condition",
"order": 0,
"condition": {
"type": "prompt",
"promptText": "Is the caller asking to speak with a human or a real person?"
}
}
```
## Transition Behavior
After each user reply, the agent evaluates edges in this priority order:
1. **Global edges** — checked first, across all global nodes. If a global condition matches, the flow jumps to that global node regardless of where the conversation currently is.
2. **Condition edges** on the current node — evaluated in `order`, first match wins
3. **Else edge** — taken if no condition edges matched
4. **Default edge** — unconditional, taken if present
5. **No match** — the agent stays in the current node and continues the conversation
This means a conversation node doesn't need a fallback edge. If no conditions match, the agent simply keeps talking at the current step — useful for nodes that need to collect information before moving on (e.g. "keep asking until the caller provides their phone number").
## Validation Rules
When submitting a flow via the API, the following rules are enforced:
1. `schemaVersion` must be `1`
2. Every node must have a unique `id`
3. `begin.startNodeId` must reference an existing node
4. All edge `source` and `target` values must reference existing nodes (except `"__global__"` as source)
5. `logic_split` nodes must have exactly one `else` edge
6. `skipResponse` conversation nodes must have exactly one `skip` edge and no other outgoing edges
7. Global nodes (`isGlobal: true`) must have at least one `__global__` edge targeting them
8. `__global__` edges must target nodes with `isGlobal: true`
9. `condition` edges must have an `order` value, unique per source node
10. Prompt conditions must have non-empty `promptText`; equation conditions must have at least one equation
11. Total flow size must not exceed 48 KB
## Complete Example
Here is a complete flow for an order status hotline:
```json theme={null}
{
"schemaVersion": 1,
"begin": {
"startNodeId": "greeting",
"whoSpeaksFirst": "agent"
},
"nodes": [
{
"id": "greeting",
"type": "conversation",
"name": "Greeting",
"position": { "x": 300, "y": 100 },
"data": {
"instructionType": "prompt",
"instruction": "Greet the caller warmly. Ask for their order number so you can look up the status."
}
},
{
"id": "extract_order",
"type": "extract_variable",
"name": "Get Order Number",
"position": { "x": 300, "y": 300 },
"data": {
"variables": [
{
"variableName": "order_number",
"description": "The customer's order number",
"variableType": "text"
}
]
}
},
{
"id": "lookup",
"type": "function",
"name": "Look Up Order",
"position": { "x": 300, "y": 500 },
"data": {
"toolName": "lookup_order",
"speakDuringExecution": true,
"speakInstruction": "One moment while I look that up.",
"speakInstructionType": "static",
"outputVariables": [
{ "outputKey": "status", "variableName": "order_status" }
]
}
},
{
"id": "check_status",
"type": "logic_split",
"name": "Check Status",
"position": { "x": 300, "y": 700 },
"data": {}
},
{
"id": "shipped_response",
"type": "conversation",
"name": "Order Shipped",
"position": { "x": 100, "y": 900 },
"data": {
"instructionType": "prompt",
"instruction": "Tell the customer their order has been shipped and ask if there's anything else you can help with."
}
},
{
"id": "pending_response",
"type": "conversation",
"name": "Order Pending",
"position": { "x": 500, "y": 900 },
"data": {
"instructionType": "prompt",
"instruction": "Let the customer know their order is still being processed and should ship within 1-2 business days. Ask if they have any other questions."
}
},
{
"id": "transfer_support",
"type": "call_transfer",
"name": "Transfer to Support",
"isGlobal": true,
"position": { "x": 700, "y": 500 },
"data": {
"transferTo": "+12025559999",
"transferMode": "cold",
"speakDuringExecution": true,
"speakInstruction": "Let me connect you with our support team right away."
}
},
{
"id": "goodbye",
"type": "end",
"name": "End Call",
"position": { "x": 300, "y": 1100 },
"data": {
"message": "Thank you for calling! Goodbye."
}
}
],
"edges": [
{
"id": "e1",
"source": "greeting",
"target": "extract_order",
"kind": "default"
},
{
"id": "e2",
"source": "extract_order",
"target": "lookup",
"kind": "default"
},
{
"id": "e3",
"source": "lookup",
"target": "check_status",
"kind": "default"
},
{
"id": "e4",
"source": "check_status",
"target": "shipped_response",
"kind": "condition",
"order": 0,
"condition": {
"type": "equation",
"match": "all",
"equations": [
{ "variable": "order_status", "operator": "==", "value": "shipped" }
]
}
},
{
"id": "e5",
"source": "check_status",
"target": "pending_response",
"kind": "else"
},
{
"id": "e6",
"source": "shipped_response",
"target": "goodbye",
"kind": "default"
},
{
"id": "e7",
"source": "pending_response",
"target": "goodbye",
"kind": "default"
},
{
"id": "e8",
"source": "__global__",
"target": "transfer_support",
"kind": "condition",
"order": 0,
"condition": {
"type": "prompt",
"promptText": "Is the caller asking to speak with a human representative?"
}
}
]
}
```
This flow:
1. **Greets** the caller and asks for their order number
2. **Extracts** the order number from the conversation
3. **Calls a tool** to look up the order status
4. **Branches** based on whether the status is `"shipped"` or anything else
5. **Responds** with the appropriate message
6. **Ends** the call
7. At **any point**, if the caller asks for a human, the call is transferred (global node)
## Importing Flows via API
Agent `mode` is set at creation time and cannot be changed afterwards. To use conversation flows, create an agent with `mode: "conversation_flow"`.
### Creating a flow agent
Use the [Create Agent](/api-reference/agents/create) endpoint with `mode` set to `"conversation_flow"` and the `flowDefinition` field set to your flow JSON:
```bash theme={null}
curl -X POST https://api.revring.ai/v1/agents \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Order Status Agent",
"mode": "conversation_flow",
"promptTemplate": "You are a helpful order status assistant.",
"flowDefinition": {
"schemaVersion": 1,
"begin": { ... },
"nodes": [ ... ],
"edges": [ ... ]
}
}'
```
### Updating a flow
To update the flow on an existing `conversation_flow` agent, use the [Update Agent](/api-reference/agents/update) endpoint with just the `flowDefinition` field:
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/{agentId} \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"flowDefinition": {
"schemaVersion": 1,
"begin": { ... },
"nodes": [ ... ],
"edges": [ ... ]
}
}'
```
`mode` is immutable after creation. You cannot convert a `single_prompt` agent to `conversation_flow` — create a new agent instead.
You can generate flow definition JSON with AI tools like ChatGPT. Share this reference page as context, describe the conversation flow you want, and paste the generated JSON into the API call or the dashboard's import feature.
## Using Variables in Flows
Flow nodes support `{{variable}}` syntax in instruction text. Variables can come from:
* **System variables** (`{{current_time}}`, `{{user_number}}`, etc.) — see [Prompting & Variables](/platform/prompting-variables)
* **Default variables** configured on your agent
* **Dynamic variables** passed via the API or pre-call webhook
* **Extracted variables** from `extract_variable` nodes
* **Tool output variables** mapped via `outputVariables` in function nodes
All variables are available to every node in the flow once set.
# Data Controls
Source: https://docs.revring.ai/platform/data-controls
Remove personal details from call transcripts and summaries before they are stored or sent to your webhook.
Each agent decides what RevRing keeps from its calls. Turn on personal-detail removal and every call's transcript and summary are cleaned before they are stored, shown in the dashboard or sent to your post-call webhook.
## Remove personal details
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/{id} \
-H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{ "storageMode": "redact_pii" }'
```
After every call, each personal detail in the transcript and summary is replaced with a label naming what was removed:
```text theme={null}
Caller: Sure, it's [person_name], [phone_number]. My date of birth is [date_of_birth].
Agent: Thanks [person_name], you're booked for Tuesday at 3pm.
```
What is removed (all of these by default):
| Category | Examples |
| - | - |
| `person_name` | A caller's first, last or full name |
| `phone_number` | Phone numbers, in digits or spoken ("four one five...") |
| `email` | Email addresses, including spoken forms ("jane at example dot com") |
| `address` | Street addresses, unit numbers, postal codes |
| `date_of_birth` | Birth dates |
| `government_id` | Social Security, passport, driver's license and tax ID numbers |
| `payment_card` | Card numbers, expiry dates and security codes |
| `bank_account` | Bank account, routing and IBAN numbers |
| `account_number` | Customer, member, policy, order and claim numbers |
| `password_or_pin` | Passwords, passcodes, PINs and security answers |
| `medical_record_number` | Medical record, patient and health plan member IDs |
To remove only some categories, list them in `piiCategories`:
```json theme={null}
{ "storageMode": "redact_pii", "piiCategories": ["payment_card", "government_id", "password_or_pin"] }
```
How it behaves:
* The redacted transcript and summary are what is stored, shown in the dashboard and sent in the post-call webhook (in every [webhook format](/platform/webhooks)). Tool call arguments and results in the transcript are redacted too.
* Your agent's own name, your business name, product names, prices and appointment times are kept.
* Extracted data (`postCallExtraction`) and evaluation results are produced from the full conversation and are **not** redacted, since they hold exactly the fields you asked for. Call variables you send are kept as you sent them.
* Recordings are not changed.
* If redaction cannot run for a call, its transcript text is withheld rather than stored with personal details.
* Calls redacted this way show `"piiRedacted": true`.
## In the dashboard
Open the agent, go to the **Advanced** tab, and set **Call data storage** to **Remove personal details from transcripts and summaries**.
# Import Agents
Source: https://docs.revring.ai/platform/importing-agents
Bring your voice agents over from Retell AI or ElevenLabs in minutes: prompt, opening, tools, transfers, voicemail, knowledge and post-call analysis, with a report of every setting.
Moving an agent from another platform should not mean rebuilding it by hand. The importer reads your agents from **Retell AI** or **ElevenLabs Agents** with your API key for that platform, shows you exactly how each setting carries over, and creates the agents here when you are ready.
## How it works
`POST /imports/agents` with the source platform and your API key for it returns the agents on that account.
`POST /imports` with the agent IDs returns a report for each agent. Nothing is created yet.
Send the same request with `"dryRun": false`. The agents are created with their custom tools and knowledge bases, and the report lists what to check before switching traffic.
```bash theme={null}
curl -X POST https://api.revring.ai/v1/imports \
-H "x-api-key: $REVRING_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "elevenlabs",
"apiKey": "",
"agentIds": ["agent_4201k6x9pq3rfe8tvb2mzk7d5wya"],
"dryRun": true
}'
```
Your source API key is used only for read-only requests to that platform during the import request. It is never stored, logged, or returned. A key with read access to agents, tools, knowledge base and voices is enough.
ElevenLabs agents that live in a data-residency region are read from that region: pass `"region": "eu"`, `"in"`, `"sg"` or `"us"`.
## The report
Every source setting that is set gets one line, so nothing is dropped silently:
| Status | Meaning |
| - | - |
| `mapped` | Carries over with the same meaning |
| `approximated` | Carried over to the closest equivalent. Worth a look before going live |
| `unsupported` | No equivalent yet. Not imported |
| `manual` | Needs a step from you, such as pasting a secret header value |
Each agent also gets a `checklist` of the steps left to you, and a `status`: `ready` or `ready_with_gaps` on a dry run, `imported` or `imported_with_gaps` after importing, `blocked` when the agent cannot be imported (see below), or `failed`.
```json theme={null}
{
"data": {
"platform": "elevenlabs",
"dryRun": true,
"agents": [
{
"source": { "platform": "elevenlabs", "agentId": "agent_4201k6...", "name": "Acme Outbound SDR" },
"status": "ready_with_gaps",
"voice": { "status": "mapped", "source": "21m00Tcm4TlvDq8ikWAM", "detail": "Studio voice \"Rachel\"" },
"customTools": [{ "name": "book_demo", "webhookUrl": "https://api.acme-crm.example.com/v1/demos" }],
"knowledgeBases": [{ "name": "Acme Outbound SDR knowledge", "sources": 2 }],
"counts": { "mapped": 31, "approximated": 6, "unsupported": 5, "manual": 3 },
"fields": [
{ "field": "conversation_config.agent.first_message", "status": "mapped", "target": "firstMessage" },
{ "field": "conversation_config.agent.prompt.llm", "status": "approximated", "target": "llmModel", "detail": "source model \"gpt-4.1\" runs on the Max tier" },
{ "field": "tools.book_demo.request_headers.X-Api-Key", "status": "manual", "target": "customTools.headers", "detail": "secret header values cannot be exported; paste the value" }
],
"checklist": [
"tools.book_demo.request_headers.X-Api-Key: secret header values cannot be exported; paste the value",
"Phone numbers: connect your numbers or SIP trunk and assign them to this agent."
]
}
],
"summary": { "agents": 1, "ready": 1, "blocked": 0, "imported": 0, "failed": 0 }
}
}
```
## What carries over
| Area | Retell AI | ElevenLabs Agents |
| - | - | - |
| Prompt | Single prompt. Multi-prompt agents are combined into one prompt with a section per stage | System prompt |
| Opening | Begin message, who speaks first, delay, wait-for-caller silence | First message (empty means the agent waits for the caller) |
| Variables | Default dynamic variables. `{{current_calendar}}` becomes `{{current_time}}` | Dynamic variable placeholders. `{{system__caller_id}}` becomes `{{user_number}}`, `{{system__called_number}}` becomes `{{agent_number}}`, `{{system__time_utc}}` becomes `{{current_time}}` |
| Model | Mapped to the Standard or Max tier, with temperature | Mapped to the Standard or Max tier, with temperature and reasoning effort |
| Speech | Language, speed, volume, interruption sensitivity, boosted keywords, noise cancellation, time zone | Language, speed, stability, keywords, interruption ignore words, time zone |
| Timing | Silence hang-up, maximum duration, reminder timeout | Turn timeout, silence hang-up, maximum duration |
| Tools | End call, transfer (fixed numbers, cold or warm with hold music, whisper, intro, caller ID and timeouts), press digit, custom webhook tools | End call, transfer to number (phone destinations, extension digits), keypad tones, voicemail detection, webhook tools (body, query and fixed values) |
| Voicemail | Hang up or leave a message | Leave a message or hang up |
| Analysis | Custom analysis fields become [extraction](/platform/webhooks) fields; call successful becomes an evaluation criterion; user sentiment becomes an extraction field | Data collection becomes extraction fields; evaluation criteria carry over |
| Knowledge | Knowledge bases with URL, text and document sources, plus retrieval settings | URL, text and file documents, plus retrieval settings |
| Webhook | Post-call webhook URL | Post-call webhook URL (the agent's own workspace webhook, or the workspace default); conversation initiation webhook becomes the pre-call webhook |
**Voices.** The importer picks the matching Studio voice when one is available, and otherwise the closest voice by language and gender. Preview the voice before going live. No platform lets you export a cloned voice, so clone it again from a recording you own.
**Webhook payloads.** An imported Retell webhook URL keeps Retell's `call_ended` / `call_analyzed` payload shape, and an ElevenLabs agent's post-call webhook keeps the `post_call_transcription` shape, so your existing handler keeps working. A webhook that is turned off on ElevenLabs is not set. Only the signature check changes: verify `X-RevRing-Signature`. See [compatibility formats](/platform/webhooks#compatibility-formats).
**Custom tool requests.** ElevenLabs webhook tools are imported with **Send Raw Request Body** on, so your endpoint receives the same body shape. Retell tools send the arguments under `arguments` instead of `args`; update the handler or turn on Send Raw Request Body.
## Not imported (yet)
* **Blocked agents:** agents that run on your own language model server (custom LLM), and Retell Conversation Flow agents. ElevenLabs workflows import the main agent prompt only.
* **Secrets:** secret header values and OAuth connections cannot be exported from the source. The tool is created with the header name and an empty value for you to fill in.
* **Phone numbers:** connect your carrier numbers or SIP trunk after importing and assign them to the agent. See [Telephony](/platform/telephony).
* Other unsupported settings, such as pronunciation dictionaries or ambient sound, are listed in the report with the reason.
## Importing again
Importing an agent you already imported **updates** the agent it created instead of making a copy: settings are re-applied, custom tools with the same name are updated, and knowledge bases already linked are left in place. Use this to pick up source changes while you test side by side.
## Limits
* Up to 25 agents per dry run, and up to 10 per import request.
* Knowledge bases hold up to 50 sources each; larger sets are split across several knowledge bases linked to the agent.
* Custom tool calls time out after 8 seconds. Longer source timeouts are reported.
See [Import Agents](/api-reference/imports/import) and [List Source Agents](/api-reference/imports/list-agents) in the API reference.
# Inbound Calls
Source: https://docs.revring.ai/platform/inbound
Configure agents to handle incoming phone calls
Configure your AI agents to answer and handle inbound phone calls on your business phone numbers.
## How Inbound Calls Work
When someone dials one of your phone numbers:
1. The call routes through your SIP trunk to RevRing
2. RevRing matches the dialed number to an agent based on phone number assignments
3. If a pre-call webhook is configured, RevRing calls it to get dynamic variables
4. The agent answers and begins the conversation using its configured prompt and first message
5. After the call ends, RevRing sends call details to your post-call webhook (if configured)
## Assigning Phone Numbers to Agents
Before an agent can receive inbound calls, you must assign at least one phone number to it.
### Step 1: Open Agent Settings
1. Navigate to [Agents](https://dashboard.revring.ai/app/agents)
2. Click on the agent you want to configure
3. Select the **Numbers** tab
### Step 2: Add an Inbound Number
1. In the **Add an Inbound Number** card, enter the phone number in E.164 format:
```
+12025551234
```
2. Click **Add Number**
The number will appear in the **Assigned Inbound Numbers** list above.
The phone number must already be assigned to your SIP trunk in both your telephony provider (Twilio, Telnyx, etc.) and in your RevRing SIP trunk configuration.
### Step 3: Verify Assignment
Call the phone number from your mobile device to verify the agent answers correctly. You should hear the agent's first message (if configured) or the agent will wait for you to speak first.
## Configuring Agent Behavior
### First Message
The first message is what the agent says immediately upon answering the call. Configure it in the **Overview** tab:
1. Navigate to the agent's **Overview** tab
2. In the **First Message** field, enter your greeting:
```
Thank you for calling Acme Support. How can I help you today?
```
3. Click **Save**
Leave the first message empty if you want the agent to wait for the caller to speak first. This is useful for support lines where callers typically explain their issue right away.
### Dynamic First Messages with Variables
You can personalize the first message using variables from the pre-call webhook:
```
Hello {{customer_name}}, thanks for calling back about {{ticket_subject}}. How can I help?
```
See the [Webhooks guide](/platform/webhooks) for details on implementing pre-call webhooks.
If the same agent also places outbound calls and needs a different opening for them, set the **Outbound First Message** field (`firstMessageOutbound`). See [Separate Inbound and Outbound Openings](/platform/outbound#separate-inbound-and-outbound-openings).
### Agent Prompt
The prompt defines how your agent behaves throughout the entire conversation. Navigate to the **Overview** tab and configure the **Prompt** field:
```
You are a customer support agent for Acme Corp. Your responsibilities:
- Listen carefully to the customer's issue
- Ask clarifying questions if needed
- Provide helpful solutions from our knowledge base
- Offer to transfer to a specialist if you cannot resolve the issue
- Always be professional, patient, and empathetic
Keep responses concise (under 30 seconds). If the customer seems frustrated, acknowledge their feelings and focus on solutions.
```
The prompt is the most critical configuration. Be specific about tone, responsibilities, and constraints. Test thoroughly and iterate based on real call transcripts.
## Advanced Inbound Features
### Pre-Call Webhooks
Pre-call webhooks allow you to customize each call based on caller information. Before answering, RevRing sends caller details to your webhook and uses the response to personalize the conversation.
Configure in the **Advanced** tab:
1. Set **Pre-call Webhook URL** to your endpoint:
```
https://api.yourcompany.com/webhooks/pre-call
```
2. Click **Save**
RevRing will POST the caller's number and other context. Your webhook must respond within 3 seconds with variables:
```json theme={null}
{
"variables": {
"customer_name": "John Smith",
"account_status": "premium",
"last_order": "Order #12345"
}
}
```
These variables can then be used in your prompt and first message with `{{variable_name}}` syntax.
See the [Webhooks guide](/platform/webhooks) for complete implementation details.
### Error Handling
If your webhook fails, times out, or returns invalid JSON:
* RevRing will proceed with the call using default variables (if configured)
* The call will not be rejected - the agent will answer without personalization
* Check the call's `preCallWebhookStatus` field in call logs to see webhook delivery status
### Default Variables
You can set default variable values in the **Overview** tab that apply to all calls unless overridden by the pre-call webhook. This is useful for agent-wide settings.
While editing your prompt, any variables you reference with `{{variable_name}}` syntax will be detected. You can set defaults in the **Default Variables** section (if visible in your dashboard configuration).
### Voicemail Detection
Enable voicemail detection to automatically handle scenarios where the agent reaches an answering machine or voicemail:
1. Navigate to the **Tools** tab
2. Locate **Voicemail Detection** under System Tools
3. Click **Configure**
4. Choose an action:
* **Hangup**: End the call immediately when voicemail is detected
* **Leave Message**: Speak a pre-recorded message and then hang up
5. If you selected "Leave Message", enter the message:
```
Hi, this is an automated message from Acme Corp. We'll try calling you back later. For immediate assistance, please call us at 1-800-555-0123.
```
6. Click **Save**
Voicemail detection is most useful for outbound calling scenarios. For inbound calls, it's typically not needed since customers are calling you.
### Call Timeouts
Configure timeout behavior in the **Advanced** tab:
* **Turn Timeout**: Maximum silence before the agent prompts the caller
```
7 seconds (recommended for natural conversation flow)
```
Set to `-1` to disable turn-level prompting.
* **Silence End Call Timeout**: Maximum total silence before ending the call
```
-1 (disabled - let caller hang up when done)
```
Or set a value like `30` to end after 30 seconds of silence.
* **Max Conversation Duration**: Hard limit on total call length
```
600 seconds (10 minutes)
```
When reached, the agent will politely end the call.
## Testing Inbound Calls
### Using the Test Panel
The test panel on the right side of the agent page lets you call your agent directly from the browser via WebRTC — no phone needed.
1. Open your agent and click the **call button** (microphone icon) in the test panel
2. Speak through your microphone to talk to the agent
3. Watch the **Transcript** update in real time
4. Click the button again to end the call
Set **Test Variables** at the bottom of the panel to simulate dynamic data your pre-call webhook would normally provide.
### Calling from a Real Phone
To test the full telephony path (SIP trunk, phone numbers, pre-call webhook):
1. Go to the **Numbers** tab and note your assigned phone numbers
2. Call one of those numbers from your mobile phone
3. Review the call in [Call Logs](https://dashboard.revring.ai/app/calls) afterwards
### Best Practices for Testing
* **Test different scenarios**: Happy path, error cases, edge cases
* **Review transcripts**: Check for misunderstandings or unexpected responses
* **Refine your prompt**: Adjust based on actual conversation flow
* **Test interruptions**: See how the agent handles being interrupted mid-sentence
* **Verify tools**: Test transfer, end call, and custom tool invocations
## Monitoring Inbound Calls
### Call Logs
View all inbound calls in the [Call Logs](https://dashboard.revring.ai/app/calls) page:
1. Filter by **Direction**: Select "Inbound"
2. Filter by **Agent**: Select your agent
3. Filter by **Status**: View completed, failed, or ongoing calls
4. Click any call to view full details, transcript, and recording
### Overview Dashboard
Monitor real-time metrics on the [Overview](https://dashboard.revring.ai/app) page:
* **Active Calls**: See how many inbound calls are currently ongoing
* **Total Calls**: Track daily, weekly, and monthly inbound call volume
* **Average Duration**: Monitor typical call length
* **Total Cost**: Track usage and costs
## Common Patterns
### Business Hours Handling
Use a pre-call webhook to check business hours and return different variables:
```json theme={null}
{
"greeting": "Thank you for calling during business hours",
"transfer_available": "true"
}
```
Or during off-hours:
```json theme={null}
{
"greeting": "Thank you for calling. Our office is currently closed",
"transfer_available": "false"
}
```
Reference these in your prompt to adjust behavior.
### IVR-Style Routing
Configure multiple agents for different departments, each with its own phone number:
* Sales: `+12025551111` → Sales Agent
* Support: `+12025552222` → Support Agent
* Billing: `+12025553333` → Billing Agent
### Escalation to Human
Enable the transfer tool to allow the agent to escalate to a human:
1. Navigate to the **Tools** tab
2. Click **Configure** on the **Transfer** tool
3. Add transfer routes:
* **To number**: `+12025559999` (your support team)
* **Condition**: `When the customer requests to speak with a human or when the issue is complex`
4. Click **Save**
The agent will automatically offer to transfer when appropriate.
## Troubleshooting
* Verify the phone number is assigned to the agent in the Phone Numbers tab
* Check that the agent status is set to Active in the Overview tab
* Confirm the number is properly configured in your SIP trunk
* Review your telephony provider's routing configuration
* Check for errors in your pre-call webhook (if configured)
* Review the call transcript in Call Logs for error messages
* Verify your prompt is not empty or malformed
* Check Advanced tab timeout settings
* Ensure the First Message field is filled in and saved
* Check that the message doesn't exceed 4000 characters
* Verify there are no syntax errors in variable references
* Review the call recording to confirm what the agent actually said
* Verify variable syntax uses double curly braces: `{{variable_name}}`
* Check that your pre-call webhook is returning the correct variable names
* Ensure the webhook response is valid JSON
* Review webhook logs for errors or timeouts
## Next Steps
Configure call transfer, end call, and custom tools
Implement pre-call and post-call webhook integrations
Set up outbound calling capabilities
Manage phone numbers programmatically via API
# Knowledge Bases
Source: https://docs.revring.ai/platform/knowledge-bases
Give your agents access to your documents, URLs, and text content for grounded, accurate answers
Knowledge bases allow your AI agents to retrieve information from your own documents during phone calls. Instead of relying solely on the system prompt, agents can search through uploaded files, web pages, and text content to provide accurate, grounded answers.
## Overview
A knowledge base is a collection of **sources** — files, URLs, or text snippets — that your agent can search during a conversation. When a caller asks a question, the agent retrieves the most relevant passages from the knowledge base and uses them to formulate a response.
This is especially useful for:
* **Product catalogs** — answer questions about pricing, features, and availability
* **FAQ documents** — handle common customer inquiries accurately
* **Policy documents** — reference specific terms, conditions, or procedures
* **Internal wikis** — provide support agents with up-to-date information
* **Training materials** — ensure consistent, accurate responses across all calls
## Creating a Knowledge Base
### From the Dashboard
1. Navigate to **Knowledge Bases** in the sidebar
2. Click **Create Knowledge Base**
3. Enter a **Name** and optional **Description**
4. Click **Create**
### Via API
```bash theme={null}
curl -X POST https://api.revring.ai/v1/knowledge-bases \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Product FAQ",
"description": "Frequently asked questions about our products"
}'
```
## Adding Sources
Each knowledge base can contain up to **50 sources**. Sources are processed asynchronously — after adding a source, it moves through the following statuses:
| Status | Description |
| - | - |
| `queued` | Source is queued for processing |
| `processing` | Source is being indexed and will be available shortly |
| `ready` | Source is indexed and available for retrieval |
| `failed` | Processing failed (check `errorMessage` for details) |
### Text Sources
Add text content directly. Useful for FAQ lists, policy excerpts, or any structured text.
**Dashboard:** Click **Add Source** → **Text**, enter a name and content, then click **Add Text Source**.
**API:**
```bash theme={null}
curl -X POST https://api.revring.ai/v1/knowledge-bases/{id}/sources \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sourceType": "text",
"displayName": "Return Policy",
"content": "Our return policy allows returns within 30 days of purchase. Items must be in original condition with receipt. Refunds are processed within 5-7 business days."
}'
```
Text sources can be up to 100,000 characters.
### URL Sources
Add a web page URL. RevRing fetches and indexes the page content automatically.
**Dashboard:** Click **Add Source** → **URL**, enter a **Name** and the **URL**, then click **Add URL Source**. Turn on **Read linked pages too** to read a whole site, and pick a schedule under **Keep it current**.
**API:**
```bash theme={null}
curl -X POST https://api.revring.ai/v1/knowledge-bases/{id}/sources \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sourceType": "url",
"originalUrl": "https://www.example.com/faq",
"displayName": "FAQ Page"
}'
```
Only `http://` and `https://` URLs are supported. Without `crawl` settings, the single page is read once, when the source is added.
#### Whole websites
Add `crawl` to read the pages a URL links to as well, so one source covers a help center or documentation site. RevRing starts at `originalUrl` and follows links on the same site under one path, up to `maxPages` pages, and combines them into one source with each page's address. Linked files (images, PDFs, scripts, feeds, text files) are not followed; add those as file sources.
```bash theme={null}
curl -X POST https://api.revring.ai/v1/knowledge-bases/{id}/sources \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sourceType": "url",
"originalUrl": "https://help.example.com/",
"displayName": "Help center",
"crawl": { "maxPages": 25, "includePath": "/articles/", "refreshDays": 7 }
}'
```
| Field | Description |
| - | - |
| `maxPages` | Pages to read, 1 to 50 (default 1: only `originalUrl`). |
| `includePath` | Only follow links whose path starts with this, for example `/help/`. Defaults to the folder of `originalUrl`, so `https://example.com/help/start` stays under `/help/`. |
| `refreshDays` | Read the pages again every 1 to 90 days. Leave it out to read them once. |
Reading a site takes up to a few minutes. When it finishes, the source's `crawl` shows how many `pages` were read and when (`lastFetchedAt`). The content of all pages counts toward one source.
#### Keeping URL sources current
With `refreshDays` set, RevRing reads the pages again on that schedule. To read a URL source again right away (after you publish a change, for example), hover the source in the dashboard and click the refresh icon, or call:
```bash theme={null}
curl -X POST https://api.revring.ai/v1/knowledge-bases/{id}/sources/{sourceId}/refresh \
-H "x-api-key: YOUR_API_KEY"
```
The refresh runs in the background (`202`). Your agents keep using the current content the whole time: new content replaces it only once it is fully indexed, and when nothing changed, nothing is replaced. If a refresh cannot read the site, the current content stays. A source can be refreshed once a minute (`429` otherwise), not while its first read is still running (`409`), and only URL sources can be refreshed (`400`).
### File Sources
Upload documents directly. Supported formats: **PDF, TXT, DOCX, MD, CSV, JSON**.
**Dashboard:** Click **Add Source** → **File**, then select a file from your computer.
**API:**
```bash theme={null}
curl -X POST https://api.revring.ai/v1/knowledge-bases/{id}/sources \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@/path/to/document.pdf"
```
Maximum file size is **20 MB**.
### Checking Source Status
After adding a source, you can check its processing status:
```bash theme={null}
curl https://api.revring.ai/v1/knowledge-bases/{id}/sources/{sourceId} \
-H "x-api-key: YOUR_API_KEY"
```
The response includes a `status` field (`queued`, `processing`, `ready`, or `failed`) and an `errorMessage` if processing failed.
## Linking Knowledge Bases to Agents
Once your knowledge base has sources in the `ready` state, link it to one or more agents.
### From the Dashboard
1. Open your agent's settings
2. Go to the **Knowledge** tab
3. Click **Link Knowledge Base**
4. Select the knowledge base you want to link — it's linked immediately
### Via API
```bash theme={null}
curl -X POST https://api.revring.ai/v1/agents/{agentId}/knowledge-bases \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"knowledgeBaseId": "kb_abc123"
}'
```
To unlink:
```bash theme={null}
curl -X DELETE https://api.revring.ai/v1/agents/{agentId}/knowledge-bases/{knowledgeBaseId} \
-H "x-api-key: YOUR_API_KEY"
```
## Retrieval Settings
When a knowledge base is linked to an agent, you can configure how the agent uses it. These settings are on the agent, not the knowledge base — so different agents can use the same knowledge base with different retrieval behaviors.
### Retrieval Mode
| Mode | Description |
| - | - |
| `automatic` | Labeled **Automatic (RAG)** in the dashboard. The agent automatically searches the knowledge base before every response. This is the default and recommended mode. |
| `query_tool` | Labeled **Query Tool** in the dashboard. The agent uses a dedicated tool to search the knowledge base. This gives you more control over when retrieval happens — useful if you want the agent to only search when explicitly asked. |
**Small knowledge bases are used in full.** In automatic mode, when the combined text of an agent's knowledge bases is small enough, the agent is simply given all of it — no search step, no chance of missing a passage, and no added latency. Larger knowledge bases (and ones built from uploaded files) use retrieval as described below. This applies across calls, web chat, and SMS.
### Chunks to Retrieve
The number of text passages to retrieve per query (1–20, default 3). Higher values provide more context but use more tokens. Start with the default and increase if the agent is missing relevant information.
### Similarity Threshold
Minimum relevance score for retrieved passages (0.0–1.0, default 0). Set this higher (e.g., 0.3–0.5) to filter out loosely related content. A value of 0 returns the top results regardless of relevance score.
### Configuring via API
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/{agentId} \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"knowledgeBaseRetrievalMode": "automatic",
"knowledgeBaseChunks": 5,
"knowledgeBaseSimilarityThreshold": 0.3
}'
```
## Managing Sources
### Deleting a Source
To remove a source from a knowledge base:
**Dashboard:** Hover over the source row and click the **trash icon**, then confirm deletion.
**API:**
```bash theme={null}
curl -X DELETE https://api.revring.ai/v1/knowledge-bases/{id}/sources/{sourceId} \
-H "x-api-key: YOUR_API_KEY"
```
### Deleting a Knowledge Base
Deleting a knowledge base removes all its sources and unlinks it from any agents.
**Dashboard:** Open the knowledge base, click the **⋮ menu**, then select **Delete Knowledge Base**.
**API:**
```bash theme={null}
curl -X DELETE https://api.revring.ai/v1/knowledge-bases/{id} \
-H "x-api-key: YOUR_API_KEY"
```
## Best Practices
### Content Quality
* **Be specific**: Focused, well-structured content retrieves better than long, rambling documents
* **Use clear headings**: Documents with clear sections help the retrieval system find relevant passages
* **Keep content current**: Set `refreshDays` on URL sources, or refresh them after your site changes; replace file and text sources when the underlying content changes
* **Avoid duplication**: Don't add the same content multiple times across different sources
### Prompt Integration
Reference the knowledge base in your agent's prompt to guide how it uses retrieved information:
```
You are a customer support agent for Acme Corp.
When customers ask about our products, policies, or procedures,
use the information from your knowledge base to provide accurate answers.
If you can't find the answer in your knowledge base, let the customer know
and offer to transfer them to a specialist.
Do not make up information — only share what you can verify from your knowledge base.
```
### Source Organization
* Create separate knowledge bases for different topics (e.g., "Product Catalog", "Support Policies", "Pricing")
* Link only the relevant knowledge bases to each agent
* Keep source count manageable — a few high-quality sources outperform many low-quality ones
## Troubleshooting
Most sources process within a few seconds to a minute; a URL source that reads a whole site can take a few minutes. If a source stays in `processing` for more than 15 minutes, it may have encountered an issue. Try deleting and re-adding the source. For file sources, ensure the file is not corrupted and is in a supported format.
Check the `errorMessage` field on the source for specifics. Common causes include:
* URL is unreachable or returns an error
* File format is not supported
* File is corrupted or empty
* Text content is empty
* Verify the knowledge base is linked to the agent (check the Knowledge tab)
* Ensure at least one source is in `ready` status
* Check that retrieval mode is set to `automatic` (or `query_tool` if you prefer manual control)
* Add guidance in your prompt telling the agent to use its knowledge base
* Try increasing the `knowledgeBaseChunks` value if relevant content is being missed
* Review the source content for accuracy
* Increase `knowledgeBaseSimilarityThreshold` to filter out loosely related passages
* Decrease `knowledgeBaseChunks` to reduce noise from less relevant results
* Add instructions in your prompt to only use verified knowledge base content
## Next Steps
Configure system and custom tools
Manage knowledge bases programmatically
Customize agent behavior with variables
Build structured conversation flows
# Messaging
Source: https://docs.revring.ai/platform/messaging
Let your agents send text messages (SMS), from the same number they call from
Connect a messaging provider so your agents can send text messages. Your agents can send an SMS **during a live call** (for example, texting the caller a booking or payment link), using the same number they call from. They can also hold [two-way text conversations](/platform/two-way-messaging) and send texts outside of a call.
## How it works
1. You connect your own messaging provider once (Twilio today).
2. You enable **Send SMS on call** for an agent and choose that connection.
3. During a call, when it's useful, the agent texts the person on the call. For example, the caller says "can you text me the link?" and the agent sends it while staying on the call.
4. The message is sent through your connected provider account.
You bring your own provider account, so messages are sent from your numbers and billed to you by your provider, at your provider's rates.
## Prerequisites
Before you begin:
* An active RevRing account with at least one configured agent
* Your own Twilio account (this can be the same account you use for voice)
* A Twilio **Messaging Service** with at least one SMS-capable number in its sender pool (recommended). Twilio's Messaging Service picks the sender for each message and handles compliance features for you.
To send texts from the **same number** your agent calls from, add that number to your Twilio Messaging Service's sender pool, or set it as the agent's send-from number in the next steps.
## Setup
### Step 1: Connect your provider
1. In the RevRing dashboard, go to [Connect → Messaging](https://dashboard.revring.ai/app/messaging).
2. Click **Connect provider** and choose **Twilio**.
3. Enter the values from your [Twilio Console](https://console.twilio.com):
* **Account SID** (starts with `AC`)
* **Auth Token**
* **Messaging Service SID** (starts with `MG`) — recommended
4. Click **Connect**.
We verify the credentials with your provider before saving them, so you'll know right away if something is off.
Your Auth Token is stored securely so we can send messages on your behalf, and it is never shown again or returned by the API. You can rotate it any time by editing the connection.
### Step 2: Enable Send SMS on an agent
1. Go to [Agents](https://dashboard.revring.ai/app/agents) and open the agent.
2. Open the **Tools** tab and find **Send SMS on call**.
3. Turn it on, then:
* **Messaging connection** — choose the connection from Step 1.
* **Send-from number** (optional) — the number texts should come from. Leave blank to let your Messaging Service choose the sender. Set a number to always send from it (it must be SMS-capable on your provider account).
* **Custom instructions** (optional) — override when and how the agent should text the caller.
4. Click **Save**.
That's it. On the agent's next call, it can text the caller when appropriate.
## Guiding when the agent texts
The agent decides when to send a text based on the conversation and its prompt. To make it reliable, mention it in your agent's prompt, for example:
```text theme={null}
If the caller asks for a link, address, or confirmation, offer to text it to them and then send it.
```
You can also use the **Custom instructions** field on the Send SMS tool to tune this behavior without touching your main prompt.
The agent can only text the person it is currently on the call with. It writes the message itself, in your agent's voice, based on what the caller asked for.
## Using the same number for calls and texts
A common goal is to have a contact see calls and texts from one number. Because your provider account owns the numbers, you control this:
* Make sure the number your agent calls from is also SMS-capable in your provider account.
* Add it to your Twilio Messaging Service sender pool, or set it as the agent's **Send-from number**.
## Manage connections via API
If you provision many agents or brands programmatically, you can manage messaging connections through the API instead of the dashboard. See [Messaging Connections](/api-reference/messaging-connections/list).
## Troubleshooting
### "We couldn't verify those credentials"
**Cause:** The Account SID or Auth Token is incorrect.
**Solution:** Re-copy both from your Twilio Console and try again. The Account SID starts with `AC`.
### "That Messaging Service SID wasn't found on the account"
**Cause:** The Messaging Service SID doesn't belong to that account.
**Solution:** Confirm the SID (starts with `MG`) under Messaging in your Twilio Console, and that it's on the same account as the Account SID you entered.
### The agent isn't texting
**Solution:**
1. Confirm **Send SMS on call** is enabled on the agent and a connection is selected.
2. Make sure the connection has a Messaging Service, or set a **Send-from number** on the agent.
3. Confirm the send-from number is SMS-capable in your provider account.
4. Encourage the behavior in your agent's prompt or the tool's custom instructions.
## Next steps
Hold text conversations and send SMS outside a call
Tell your agent when to text the caller
See all built-in and custom agent tools
Manage connections programmatically
Open the dashboard
# Multilingual
Source: https://docs.revring.ai/platform/multilingual
Build AI voice agents that speak multiple languages
RevRing agents speak and understand 20 languages, plus a multilingual mode for callers who switch languages, with 300+ voices.
## Supported Languages
The agent's `language` setting chooses how the agent listens and speaks:
| Language | Code | Language | Code |
| - | - | - | - |
| English | `en` | Arabic | `ar` |
| Spanish | `es` | Russian | `ru` |
| French | `fr` | Polish | `pl` |
| Chinese | `zh` | Dutch | `nl` |
| Korean | `ko` | Hindi | `hi-IN` |
| Japanese | `ja` | Bengali | `bn-IN` |
| German | `de` | Tamil | `ta-IN` |
| Italian | `it` | Telugu | `te-IN` |
| Portuguese | `pt` | Gujarati | `gu-IN` |
| Multilingual | `multi` | Kannada, Marathi | `kn-IN`, `mr-IN` |
### Multilingual mode
With `"language": "multi"` the agent understands callers speaking English, Spanish, French, German, Italian, Portuguese, Dutch, Russian, Hindi or Japanese, including callers who switch mid-call, and replies in the language the caller speaks. Tell the agent how to choose in the prompt, for example: *"Reply in the language the caller uses. If they switch to Spanish, continue in Spanish."* Pick a voice that sounds natural in every language you expect.
Voices are also available in Turkish, Greek, Ukrainian, Danish, Estonian, Finnish, Norwegian, Swedish and Vietnamese; preview them in the [Voice Library](https://dashboard.revring.ai/app/agents/voices).
**Voice Flexibility:** Any voice can be used with any language setting. For example, you can use a Spanish-labeled voice on an English-language agent to achieve a Spanish accent, or mix and match voices and languages to create the exact speaking style you need.
## Setting Agent Language
### Via Dashboard
1. Navigate to [Agents](https://dashboard.revring.ai/app/agents) and select your agent
2. Go to the **Overview** tab
3. Select your desired **Language** from the dropdown and click **Save**
4. Go to the **Voice** tab
5. Choose a **Voice** that matches your selected language
6. Click **Save Voice**
You can preview all available voices in the [Voice Library](https://dashboard.revring.ai/app/agents/voices).
### Via API
Set the language when creating or updating an agent:
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/agent_123abc \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"language": "es",
"voiceId": "revring-rafael"
}'
```
**Supported language codes:**
* `en` (English)
* `es` (Spanish)
* `pt` (Portuguese)
* `de` (German)
* `it` (Italian)
* `nl` (Dutch)
* `zh` (Chinese)
* `ja` (Japanese)
* `hi` (Hindi)
* `tr` (Turkish)
* `el` (Greek)
* `uk` (Ukrainian)
* `ar` (Arabic)
* `da` (Danish)
* `et` (Estonian)
* `fi` (Finnish)
* `nb` (Norwegian Bokmål)
* `no` (Norwegian)
* `sv` (Swedish)
* `vi` (Vietnamese)
## One agent, many languages
Use one agent for callers in different languages: set the language per call, and give the agent a voice and opening line for each language.
### Language per call
When you know the caller's language before the call (from your CRM, for example), pass it when you place the call. The agent listens and speaks in that language for this call only:
```bash theme={null}
curl -X POST https://api.revring.ai/v1/calls \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "AGENT_ID",
"toNumber": "+15551234567",
"fromNumber": "+15559876543",
"language": "es"
}'
```
For inbound calls, your [pre-call webhook](/platform/webhooks) can answer with `"overrides": { "language": "es" }` to run that call in Spanish. The `{{language}}` variable carries the call's language.
### Language presets
`languagePresets` gives the agent a voice and an opening line per language. When a call runs in a language that has a preset, the agent uses that preset's voice and opening line instead of its own:
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/{agentId} \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"languagePresets": {
"es": { "voiceId": "SPANISH_VOICE_ID", "firstMessage": "Hola {{first_name}}, le habla Maya de Northside." },
"fr": { "voiceId": "FRENCH_VOICE_ID", "firstMessage": "Bonjour, ici Maya de Northside." }
}
}'
```
* A preset can set the voice, the opening line, or both. Whatever it leaves out comes from the agent.
* A regional code uses its base language's preset when it has none of its own: a call in `pt-BR` uses the `pt` preset.
* A voice or opening line given explicitly for one call (per-call `voiceId`, or the pre-call webhook's `voiceId` or `firstMessage`) wins over the preset.
* Preset voices come from the [Voice Library](https://dashboard.revring.ai/app/agents/voices) (`GET /v1/voices`); unknown voices are refused.
* Agents imported from ElevenLabs bring their per-language opening lines.
## Writing Prompts for Multilingual Agents
### Best Practice: Match Prompt Language
For optimal performance, write your agent's prompt in the same language as the agent's configured language setting:
**Spanish Agent Example:**
```
Eres un asistente de servicio al cliente profesional y amigable.
INSTRUCCIONES:
- Saluda a los clientes calurosamente
- Responde preguntas sobre nuestros productos
- Mantén las respuestas concisas y útiles
Siempre sé cortés y profesional.
```
### Cross-Language Prompts
While not required, agents can follow prompts written in a different language than their speaking language. The AI is intelligent enough to understand instructions in one language while conversing in another:
**English Prompt for Spanish Agent:**
```
You are a customer service agent speaking Spanish.
INSTRUCTIONS:
- Greet customers warmly in Spanish
- Answer questions about our products
- Keep responses under 30 seconds
Always be polite and professional.
```
However, for best results and consistency, we recommend writing prompts in the agent's configured language.
## Language System Variable
The `{{language}}` system variable is automatically available in all prompts and contains the agent's language code:
```
You are speaking in {{language}}.
Current language: {{language}}
Timezone: {{timezone}}
Current time: {{current_time}}
```
This can be useful for building flexible prompts or logging purposes.
## Voice Selection
Each language has multiple voices to choose from. Visit the [Voice Library](https://dashboard.revring.ai/app/agents/voices) to preview all available voices in your selected language.
### Backup Voice
Every agent can set a **Backup Voice** (in the dashboard Voice tab, or `fallbackVoiceId` via the API) for seamless voice continuity. In the rare case a voice is temporarily unavailable, the agent switches to its backup voice instantly and the conversation continues as normal. Leave it on **Automatic** to let the platform manage this for you. For multilingual agents we recommend choosing a backup voice in the agent's own language.
### English Voice Categories
English offers two categories of voices:
* **English Ultra-Realistic (Beta)**: Cutting-edge voices with exceptional naturalness and expressiveness
* **English Stable**: Reliable, production-ready voices with consistent performance
If you experience instability with Ultra-Realistic voices, try switching to an **English Stable** voice for better reliability.
## Troubleshooting
### Voice Instability
If you notice inconsistent voice quality or unexpected behavior:
1. **Switch Voice Category**: For English agents, try switching from Ultra-Realistic to Stable voices
2. **Verify Language Match**: Ensure the selected voice matches your agent's language setting
### Need Help?
For assistance with multilingual setup, voice selection, or configuration, contact support.
Our team can help you:
* Choose the right voices for your use case
* Optimize prompts for specific languages
* Configure multilingual routing strategies
* Troubleshoot voice quality issues
## Complete Example
Here's a complete Spanish agent configuration:
**Via API:**
```bash theme={null}
curl -X POST https://api.revring.ai/v1/agents \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sipTrunkId": "trunk_abc123",
"name": "Asistente en Español",
"language": "es",
"voiceId": "revring-rafael",
"promptTemplate": "Eres un asistente de servicio al cliente. Ayuda al cliente con sus preguntas.",
"firstMessage": "Hola, bienvenido. ¿Cómo puedo ayudarte hoy?"
}'
```
**Via Dashboard:**
1. Create agent with name "Asistente en Español"
2. In **Overview** tab:
* Language: Spanish
* First Message: "Hola, bienvenido. ¿Cómo puedo ayudarte hoy?"
* Prompt: "Eres un asistente de servicio al cliente. Ayuda al cliente con sus preguntas."
3. In **Voice** tab:
* Voice: Rafael
For callers who switch languages mid-call, use multilingual mode. For a market that speaks one language, a dedicated agent in that language with a native voice gives the best quality.
# Outbound Calls
Source: https://docs.revring.ai/platform/outbound
Send outbound calls through your AI agents
Configure your AI agents to initiate outbound phone calls for sales, surveys, reminders, and more.
## How Outbound Calls Work
When you send an outbound call through RevRing:
1. You specify the agent, from number (caller ID), to number (recipient), and optional variables
2. RevRing initiates the call through your SIP trunk
3. When the recipient answers, the agent begins speaking based on its first message and prompt
4. The conversation proceeds according to your agent's configuration
5. After the call ends, RevRing sends complete call details to your post-call webhook (if configured)
## Sending Test Calls via Dashboard
### Using the Send Call Page
The [Send Call](https://dashboard.revring.ai/app/testing/send) page is the primary way to test outbound calls from the dashboard.
1. Navigate to [Send Call](https://dashboard.revring.ai/app/testing/send)
2. Select your **Agent** from the dropdown
3. Enter the **From Number** (must be a number on your SIP trunk, E.164 format)
4. Enter the **To Number** (the recipient's phone number, E.164 format)
5. Optionally set a **Caller ID Name** (up to 15 characters)
6. Add **Variables** if your prompt uses dynamic content
7. Click **Send Test Call**
The **Live Transcript** card on the right updates in real time as the conversation progresses. After the call ends, click **View full call details** to see the recording, summary, and metrics.
Save frequently used configurations by entering a name and clicking **Save Config**. Load saved configs from the dropdown to quickly re-run tests with different scenarios.
### Using the Test Panel
The test panel on the right side of the agent page lets you call your agent directly from the browser via WebRTC. This is useful for quick iteration — you can tweak the prompt and immediately test without needing a phone. Use **Test Variables** to set `direction`, `customer_name`, or any other variables your prompt expects.
To test outbound calling over the phone network (with real caller ID and SIP routing), use the **Send Call** page instead.
## Sending Calls via API
For production outbound calling at scale, use the RevRing API.
### Basic Outbound Call
```bash theme={null}
curl -X POST https://api.revring.ai/v1/calls \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_id_here",
"fromNumber": "+12025551234",
"toNumber": "+14155552671"
}'
```
### With Caller ID Name
You can set a custom caller ID name (up to 15 characters) that is displayed to the recipient when the call arrives:
```bash theme={null}
curl -X POST https://api.revring.ai/v1/calls \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_id_here",
"fromNumber": "+12025551234",
"toNumber": "+14155552671",
"callerIdName": "Acme Support"
}'
```
Caller ID name support depends on your SIP trunk provider and the recipient's carrier. Not all carriers display the caller ID name.
### With Custom Variables
```bash theme={null}
curl -X POST https://api.revring.ai/v1/calls \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_id_here",
"fromNumber": "+12025551234",
"toNumber": "+14155552671",
"variables": {
"customer_name": "John Smith",
"appointment_time": "2:00 PM today",
"confirmation_code": "ABC123"
}
}'
```
The API returns a call object with an `id` that you can use to track the call's status.
See the [API Reference](/api-reference/calls/send) for complete documentation.
## Configuring Outbound Agent Behavior
### First Message
For outbound calls, the first message is critical since you're initiating contact. Configure it in the **Overview** tab:
```
Hi {{customer_name}}, this is Alex calling from Acme Corp regarding your appointment scheduled for {{appointment_time}}. Is now a good time to talk?
```
Unlike inbound calls where leaving first message empty makes sense, outbound calls should almost always include a first message to properly introduce the purpose of the call.
### When the Phone Is Still Ringing
Some carrier routes report a call as answered before the person has actually picked up, occasionally by 15 seconds or more. Anything the agent says in that window is spoken into a ringing phone, and the person hears nothing or only the tail of the opening. Two settings control this, and both default to current behavior so existing agents are unchanged.
**Pause before speaking.** Set `firstMessageDelayMs` to wait that many milliseconds after the call is reported answered before the opening is spoken. Tune it to the route: start around 2000 to 3000 and adjust. If the person speaks during the pause, the opening is skipped and the agent simply replies to them, so the two never talk over each other.
```json theme={null}
{ "firstMessageDelayMs": 2500 }
```
**Let the caller speak first.** Set `startSpeaker` to `"user"` and the agent stays silent until the person says something, then opens the conversation from its prompt. Nothing is spoken during ringing at all. The re-engagement prompt is held as well, so the silence is genuine rather than a short delay.
```json theme={null}
{ "startSpeaker": "user" }
```
**If the person never speaks.** When the caller speaks first, set `beginAfterUserSilenceMs` and the agent opens the call itself once the person has said nothing for that many milliseconds (500 to 60000). If they speak first, the agent replies to them as usual. With a static first message the agent speaks it; otherwise it generates its opening from the prompt. Leave it unset to keep waiting. Keep it shorter than the silence end call timeout, otherwise a silent call ends before the agent speaks.
```json theme={null}
{ "startSpeaker": "user", "beginAfterUserSilenceMs": 4000 }
```
Use the pause when you want the agent to open the call but slightly later, and `startSpeaker` when you would rather the person open it. A third option needs no settings at all: keep the opening to a short line such as "Hi, is this Alex?" so that little is lost if part of it is missed.
### Different Opening Settings for Outbound Calls
An agent that handles both directions can open each one differently: greet inbound callers straight away, but wait for the person to say "Hello?" on calls it places. `startSpeakerOutbound` and `firstMessageDelayMsOutbound` apply to outbound calls only. Left unset, outbound calls use `startSpeaker` and `firstMessageDelayMs` like every other call, so existing agents are unchanged. In the dashboard, tick **Use different opening settings for outbound calls** on the **Overview** tab.
```json theme={null}
{
"startSpeaker": "agent",
"startSpeakerOutbound": "user",
"beginAfterUserSilenceMs": 4000
}
```
### Per-Call Opening Overrides
`POST /v1/calls` accepts `startSpeaker`, `firstMessageDelayMs` and `beginAfterUserSilenceMs` to change how a single call opens, for example a longer pause for a list known to answer late, without editing the agent. For each setting, a value sent with the call takes precedence over the agent's outbound setting, which takes precedence over its general setting. The agent's saved settings are unchanged.
```json theme={null}
{
"agentId": "agent_123",
"toNumber": "+15551234567",
"fromNumber": "+15557654321",
"startSpeaker": "user",
"beginAfterUserSilenceMs": 3000
}
```
### Separate Inbound and Outbound Openings
Agents that handle both directions often need different openings: "Thank you for calling..." inbound versus "Hi, this is ... calling from ..." outbound. Set the **Outbound First Message** field (API: `firstMessageOutbound`) with the outbound text, and keep **First Message** as the inbound text. On outbound calls the agent speaks the outbound text; on inbound calls (or when the outbound field is empty) it speaks First Message, so existing agents are unaffected until you set it.
Both fields support `{{variables}}` with the same substitution as the prompt, so per-call personalization works in either direction. This applies to static first messages; in dynamic mode the opening is generated from the prompt, which can branch on the `{{direction}}` variable instead.
### Outbound-Specific Prompts
Tailor your prompt for outbound scenarios:
```
You are making an outbound call to confirm a customer's appointment. Your goals:
1. Verify the customer is available to talk (ask permission)
2. Confirm their appointment for {{appointment_time}}
3. Answer any questions about the appointment
4. Offer to reschedule if needed
5. Thank them and end the call politely
If they ask to be removed from future calls, acknowledge and confirm you'll update their preferences.
Keep the call brief (under 2 minutes) and respectful of their time.
```
### Using Variables for Personalization
Variables allow you to customize each call without changing the agent configuration:
1. Reference variables in your prompt and first message using `{{variable_name}}` syntax
2. Pass variable values when sending the call via API or dashboard
3. The agent will replace placeholders with the actual values during the conversation
Common variable use cases:
* Customer name, account number, order ID
* Appointment dates and times
* Custom messages or offers
* Callback reasons or context
## Voicemail Detection
Voicemail detection is particularly useful for outbound calling to avoid wasting resources on unanswered calls.
### Enable Voicemail Detection
1. Navigate to the **Tools** tab
2. Locate **Voicemail Detection** under System Tools
3. Click **Configure**
4. Choose your action:
* **Hangup**: End the call immediately when voicemail is detected (saves costs)
* **Leave Message**: Speak a pre-recorded message before hanging up
5. If leaving a message, configure the voicemail message:
```
Hi {{first_name}}, this is Acme Corp calling to confirm your appointment. Please call us back at 1-800-555-0123 at your earliest convenience. Thank you.
```
The voicemail message supports `{{variables}}`, just like your prompt and first message. Variables are filled in from the call's variables (for example, `first_name`). If a variable has no value, the placeholder is left as-is.
6. Click **Save**
For high-volume outbound campaigns, choose "Hangup" to minimize costs and focus agent time on live conversations.
### When Nobody Speaks
Some voicemail systems answer without any greeting the agent can hear, so there is nothing for voicemail detection to key on. The platform closes these calls automatically: if nobody has spoken by the third re-engagement attempt (and the call is at least 20 seconds old), the agent says a short sign-off and ends the call with hangup cause `SILENCE_TIMEOUT`. This applies only while the other side has said nothing at all. Once a person has spoken, the agent stays patient through quiet stretches and keeps re-engaging normally.
## Call Pacing and Concurrency
### Understanding Concurrency Limits
RevRing enforces a concurrency limit on the maximum number of simultaneous calls per organization. You can view your current limit and usage on the [Billing page](https://dashboard.revring.ai/app/billing).
### Call Queuing
When you send an outbound call and all concurrent call slots are in use, RevRing automatically **queues** the call. Queued calls are dispatched in approximate order as slots become available.
While a call is waiting, its status is `QUEUED`. Once a slot opens and the call is dispatched, the status transitions to `INITIATED` and then `ONGOING` when the recipient answers.
Queued calls have a maximum wait time of approximately 4 hours. If a slot does not open within that window, or if the call fails to dispatch for another reason, the call transitions to `FAILED` rather than remaining `QUEUED` indefinitely. Inspect the `errorMessage` field to see why.
You can check a call's status at any time:
```bash theme={null}
curl https://api.revring.ai/v1/calls/{callId} \
-H "x-api-key: YOUR_API_KEY"
```
To cancel a queued call before it is dispatched:
```bash theme={null}
curl -X POST https://api.revring.ai/v1/calls/{callId}/cancel \
-H "x-api-key: YOUR_API_KEY"
```
Only calls with status `QUEUED` can be cancelled. Once a call moves to `INITIATED` or `ONGOING`, it cannot be cancelled through this endpoint.
### Opting Out of Queuing
If you prefer to handle queuing in your own system, pass `skipQueue: true` when creating the call. This tells RevRing to return an immediate `429` error instead of queuing when the concurrency limit is reached:
```bash theme={null}
curl -X POST https://api.revring.ai/v1/calls \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_id_here",
"fromNumber": "+12025551234",
"toNumber": "+14155552671",
"skipQueue": true
}'
```
**Response when at capacity (429)**:
```json theme={null}
{
"error": "concurrency_limit"
}
```
### Best Practices for High Volume
* **Leverage built-in queuing**: Let RevRing queue calls automatically rather than managing pacing yourself
* **Monitor active calls**: Track concurrent calls via the [Analytics API](/api-reference/analytics/overview)
* **Cancel unnecessary queued calls**: If priorities change, cancel queued calls that are no longer needed
* **Handle errors gracefully**: Implement retry logic with exponential backoff for non-queued calls
* **Respect time zones**: Schedule calls during appropriate hours for the recipient's location
## Monitoring Outbound Calls
### Real-Time Monitoring
View active outbound calls on the [Overview](https://dashboard.revring.ai/app) page:
* **Active Calls**: See current ongoing outbound calls
* Filter by agent to monitor specific campaigns
### Call Logs
Track all outbound calls in [Call Logs](https://dashboard.revring.ai/app/calls):
1. Filter by **Direction**: Select "Outbound"
2. Filter by **Status**: View completed, failed, ongoing, or canceled calls
3. Filter by **Agent**: Focus on specific agents or campaigns
4. Click any call to view:
* Complete transcript
* Call recording
* Duration and timestamps
* Variables used
* Hangup cause and error messages (if applicable)
### Success Metrics
Monitor key metrics to optimize your outbound campaigns:
* **Connection rate**: Percentage of calls that connect vs. no answer/busy
* **Average duration**: Typical call length for successful connections
* **Completion rate**: Calls that reach the intended outcome
* **Cost per call**: Track expenses across campaigns
## Advanced Features
### Post-Call Webhooks
Receive call results immediately after each call ends:
1. Configure **Post-call Webhook URL** in the **Advanced** tab:
```
https://api.yourcompany.com/webhooks/post-call
```
2. RevRing will POST complete call details including:
* Transcript
* Recording URL
* Call duration and timestamps
* Variables used
* AI-generated summary
Use this to:
* Update your CRM with call outcomes
* Trigger follow-up actions
* Track campaign performance
* Schedule callbacks for unsuccessful attempts
See the [Webhooks guide](/platform/webhooks) for implementation details.
### Call Transfer
Enable transfers to allow the agent to escalate to a human representative:
1. Navigate to the **Tools** tab
2. Click **Configure** on the **Transfer** tool
3. Add transfer routes:
* **To number**: `+12025559999` (your sales team)
* **Condition**: `When the customer wants to speak with a human or has complex questions`
4. Click **Save**
The agent will offer to transfer when the condition is met.
### End Call Control
Configure when the agent should end calls:
1. Navigate to the **Tools** tab
2. Click **Configure** on the **End Call** tool
3. Set custom end call rules:
```
End the call when:
- The customer confirms their appointment
- The customer declines and asks not to be called again
- The customer is not available and asks to call back later
- You've been speaking for more than 3 minutes
```
4. Click **Save**
## Compliance and Best Practices
### Legal Requirements
When making outbound calls, ensure compliance with:
* **TCPA** (US): Obtain prior express written consent for marketing calls
* **TSR** (US): Maintain do-not-call lists and honor opt-out requests (RevRing can [add callers who ask automatically](/platform/campaigns#add-callers-who-ask-automatically))
* **GDPR** (EU): Respect data protection and privacy regulations
* **Local regulations**: Follow country-specific telemarketing laws
You are responsible for ensuring compliance with all applicable laws and regulations. RevRing provides the technical platform but does not provide legal advice.
### Best Practices
* **Respect time zones**: Call during appropriate hours (typically 9 AM - 9 PM local time)
* **Honor opt-outs**: Immediately stop calling anyone who requests removal
* **Clear identification**: Always identify your company and purpose clearly
* **Keep it brief**: Respect the recipient's time with concise, focused calls
* **Professional tone**: Use appropriate language and maintain a professional demeanor
* **Quality over quantity**: Focus on successful conversations, not just call volume
## Common Use Cases
### Appointment Reminders
```
Hi {{customer_name}}, this is Acme Dental calling to remind you about your appointment tomorrow at {{appointment_time}} with Dr. {{doctor_name}}.
Please respond with "confirm" if you'll be able to make it, or let me know if you need to reschedule.
```
### Sales Follow-Up
```
Hi {{lead_name}}, this is {{agent_name}} from {{company_name}}. You recently expressed interest in {{product_name}} on our website.
I'm calling to see if you have any questions and to share how we can help {{solve_problem}}.
```
### Survey and Feedback
```
Hi {{customer_name}}, this is a brief call from Acme Corp. We recently helped you with {{service_type}} and would love to get your feedback.
On a scale of 1 to 10, how satisfied were you with our service?
```
### Payment Reminders
```
Hi {{customer_name}}, this is Acme Billing. We're calling about invoice {{invoice_number}} with a balance of {{amount}} due on {{due_date}}.
Are you able to make a payment today, or would you like to discuss payment options?
```
## Troubleshooting
* Verify the from number is assigned to your SIP trunk
* Ensure the to number is in valid E.164 format
* Check your SIP trunk has sufficient capacity and balance
* Review your telephony provider's outbound restrictions
* Confirm the destination country is allowed by your provider
* Ensure voicemail detection is enabled in the Tools tab
* Verify the action (hangup or leave message) is configured
* Check the call recording to confirm voicemail was actually reached
* Some voicemail systems may not be detectable - this is a known limitation
* Verify variable names match exactly between API call and prompt (case-sensitive)
* Check that variables are passed in the API request body
* Ensure variable syntax in prompt uses double curly braces: `{{variable}}`
* Review the call transcript to see what was actually spoken
* Check if you're calling valid, reachable numbers
* Verify caller ID (from number) is not flagged as spam
* Consider time of day - avoid early morning or late evening
* Review hangup causes in call logs to identify patterns
* Ensure your first message clearly identifies the caller
* Implement exponential backoff and retry logic
* Reduce call pacing or batch size
* Check your current limit and usage at [Billing](https://dashboard.revring.ai/app/billing)
* Monitor active calls before sending new batches
* Contact support to discuss increasing your organization's limit
## Next Steps
Configure transfer, voicemail, and custom tools
Implement post-call webhooks to capture results
View complete API documentation for sending calls
Query and analyze call history via API
# Prompting & Variables
Source: https://docs.revring.ai/platform/prompting-variables
Personalize agent conversations with dynamic variables
Variables enable you to personalize agent prompts and messages with dynamic data. RevRing provides system variables automatically and supports custom variables that you can set through the API or webhooks.
## Variable Types
RevRing supports three types of variables:
* **System Variables**: Automatically provided for every call with contextual information
* **Default Variables**: Static variables you configure on the agent that apply to all calls
* **Dynamic Variables**: Call-specific variables set per outbound call or via pre-call webhook for inbound calls
Variables merge with the following priority: system variables are set first, then default variables are applied, and finally dynamic variables can override both system and default variables.
## System Variables
System variables are automatically available in every call and provide contextual information about the call itself.
| Variable | Description | Example |
| - | - | - |
| `{{direction}}` | Whether the call is inbound or outbound | `inbound` or `outbound` |
| `{{user_number}}` | The customer's phone number | `+14155551234` |
| `{{agent_number}}` | Your agent's phone number (caller ID for outbound) | `+12025559999` |
| `{{current_time}}` | Current timestamp when the call starts, formatted in the agent's timezone | `Tuesday, November 26, 2025 at 2:30:00 PM EST` |
| `{{timezone}}` | The agent's IANA timezone identifier | `America/New_York` |
| `{{language}}` | The agent's language code | `en` |
System variables are always available for you to use in your agent's prompt and first message.
### Example Usage
```
You are a customer service agent.
CALL INFORMATION:
- Call direction: {{direction}}
- Customer phone: {{user_number}}
- Our number: {{agent_number}}
- Current time: {{current_time}}
Greet the customer and provide assistance based on the {{direction}} call context.
```
## Default Variables
Default variables are static values you configure on your agent that apply to every call. These are useful for consistent information like business name, hours, or policies.
### Setting Default Variables
Configure default variables via the [Update Agent API](/api-reference/agents/update):
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/agent_123abc \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"defaultVariables": {
"business_name": "Acme Corp",
"business_hours": "Monday-Friday 9am-5pm EST",
"support_email": "support@acme.com"
}
}'
```
These variables will be available in your prompt as `{{business_name}}`, `{{business_hours}}`, and `{{support_email}}`.
### Example Prompt with Default Variables
```
You are the receptionist for {{business_name}}.
INFORMATION:
- Business hours: {{business_hours}}
- Support email: {{support_email}}
Answer customer questions about our services and schedule appointments during business hours.
```
## Dynamic Variables
Dynamic variables are set on a per-call basis and override default variables. The method for setting them differs between outbound and inbound calls.
### For Outbound Calls
Set dynamic variables when sending an outbound call via the [Send Call API](/api-reference/calls/send):
```bash theme={null}
curl -X POST https://api.revring.ai/v1/calls \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_123abc",
"fromNumber": "+12025559999",
"toNumber": "+14155551234",
"variables": {
"customer_name": "John Smith",
"appointment_time": "2:00 PM today",
"confirmation_code": "ABC123"
}
}'
```
### For Inbound Calls
Set dynamic variables by returning them from your pre-call webhook. See the [Webhooks guide](/platform/webhooks#pre-call-webhooks) for implementation details.
Your webhook receives caller information and returns variables:
```json theme={null}
{
"variables": {
"customer_name": "Sarah Johnson",
"account_status": "premium",
"last_order_date": "March 10, 2024"
}
}
```
## Complete Example
Here's a comprehensive example showing all variable types in action:
### Agent Configuration
**Default Variables** (via API):
```json theme={null}
{
"business_name": "Dr. Smith's Dental Office",
"office_hours": "Monday-Friday 8am-6pm"
}
```
**Prompt**:
```
You are an appointment booking assistant for {{business_name}}.
CALL INFORMATION:
- Call direction: {{direction}}
- Customer phone: {{user_number}}
- Office phone: {{agent_number}}
- Current time: {{current_time}}
OFFICE DETAILS:
- Hours: {{office_hours}}
YOUR TASK:
Help {{customer_name}} schedule a dental appointment.
GREETING:
Start by greeting {{customer_name}} warmly and confirming you're speaking with them at {{user_number}}.
CONVERSATION FLOW:
1. Greet {{customer_name}} and mention you see they're calling from {{user_number}}
2. Ask what type of appointment they need (cleaning, checkup, emergency, etc.)
3. Check their preferred date and time
4. Confirm the appointment details including:
- Patient name: {{customer_name}}
- Callback number: {{user_number}}
- Appointment date and time
- Type of appointment
5. Let them know they'll receive a confirmation text at {{user_number}}
Keep responses concise and professional. The current time is {{current_time}}, so suggest appropriate appointment slots during {{office_hours}}.
```
**First Message**:
```
Hi {{customer_name}}, this is Alex from {{business_name}}. I see you're calling from {{user_number}}. How can I help you today?
```
### Setting Dynamic Variables
**For outbound calls:**
```bash theme={null}
curl -X POST https://api.revring.ai/v1/calls \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_123abc",
"fromNumber": "+12025559999",
"toNumber": "+14155551234",
"variables": {
"customer_name": "John Smith"
}
}'
```
**For inbound calls** (pre-call webhook response):
```json theme={null}
{
"variables": {
"customer_name": "Sarah Johnson"
}
}
```
### Where Variables Are Substituted
`{{variable}}` placeholders are replaced in all of the following fields:
* Agent prompt
* First message
* Voicemail message
* Warm transfer fields: hold message, summary instructions (the briefing spoken to the person you transfer to), and intro message
Substitution uses the variables available on that specific call: system variables, the agent's default variables, and the per-call variables you send. A placeholder whose variable is not set on the call is left exactly as typed (for example `{{external_id}}` stays visible), so missing data is easy to spot rather than silently disappearing. Make sure every variable referenced in these fields is supplied on each call.
## First Message
The first message is the line your agent speaks when the call connects. It has two modes, controlled by the `firstMessageMode` field on the agent.
### Static (default)
The agent speaks the `firstMessage` text exactly as written. It supports `{{variables}}`, just like the prompt:
```
Hi {{customer_name}}, this is Alex from {{business_name}}. How can I help you today?
```
Leave `firstMessage` empty to have the agent wait for the caller to speak first.
### Dynamic
Set `firstMessageMode` to `dynamic` and the agent generates its opening line from the prompt instead of speaking a fixed string. This is useful when a single agent handles both inbound and outbound calls and needs a different greeting for each, since the `{{direction}}` system variable is available in the prompt:
```
You are a scheduling assistant for {{business_name}}.
When the call begins, greet the caller:
- If {{direction}} is inbound, thank them for calling {{business_name}} and ask how you can help.
- If {{direction}} is outbound, introduce yourself and explain you are calling to confirm their appointment.
```
Enable it via the API:
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/agent_123abc \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"firstMessageMode": "dynamic"
}'
```
In dynamic mode the `firstMessage` text is ignored. To return to a fixed greeting, set `firstMessageMode` back to `static` (or `null`).
## Working with Timezones
RevRing automatically handles timezone conversion for you. The `{{current_time}}` system variable is formatted in your agent's configured timezone, and the `{{timezone}}` variable contains the IANA timezone identifier.
### Setting Agent Timezone
Configure your agent's timezone via the API when creating or updating an agent:
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/agent_123abc \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"timezone": "America/New_York"
}'
```
Or set it through the RevRing dashboard in your agent's configuration.
### Using Timezone in Prompts
Both `{{current_time}}` and `{{timezone}}` are automatically available as system variables:
```
You are an appointment scheduling assistant for {{business_name}}.
CURRENT TIME:
{{current_time}}
Your timezone is {{timezone}}. All times you communicate should be in this timezone.
When booking appointments:
1. Use the current time above as your reference
2. Suggest available slots in {{timezone}}
3. Always mention the timezone when confirming (e.g., "2 PM Eastern Time")
```
### Timezone Format
Use IANA timezone identifiers (e.g., `America/New_York`, `Europe/London`, `Asia/Tokyo`) as they handle daylight saving time automatically.
Common timezone identifiers:
* **US Eastern**: `America/New_York`
* **US Central**: `America/Chicago`
* **US Mountain**: `America/Denver`
* **US Pacific**: `America/Los_Angeles`
* **UK**: `Europe/London`
* **Central Europe**: `Europe/Paris`
* **India**: `Asia/Kolkata`
* **Australia (Sydney)**: `Australia/Sydney`
### Complete Example
```
You are scheduling appointments for {{business_name}}.
CURRENT TIME & TIMEZONE:
The current time is: {{current_time}}
Your timezone: {{timezone}}
Business hours: {{business_hours}}
INSTRUCTIONS:
1. Reference {{current_time}} when discussing availability
2. All appointment times should be stated in {{timezone}}
3. When confirming appointments, explicitly mention the timezone
4. If the customer mentions a different timezone, acknowledge it but confirm appointments in {{timezone}}
Example:
Agent: "I have availability tomorrow at 2 PM, 3:30 PM, and 5 PM Eastern Time. Which works best for you?"
Customer: "2 PM works"
Agent: "Perfect! I've scheduled you for 2 PM Eastern Time on [date]. You'll receive a confirmation."
```
Set your agent's timezone to match your business location or primary customer base. For businesses serving multiple timezones, you can create separate agents for different regions, or override the system variables on a per-call basis when sending or receiving a call.
The `{{current_time}}` variable is formatted in a human-readable format like "Wednesday, November 26, 2025 at 8:30:00 PM EST" to make it easy for the AI to understand and communicate naturally.
## Boosting Recognition of Names and Custom Terms
Speech recognition can struggle with uncommon personal names, brand names, and industry-specific terms. You can boost recognition accuracy for terms that matter on a call in two ways:
### Automatic name boosting
Any call variable whose key contains `name` (for example `first_name`, `last_name`, `company_name`, `attorney_name`) is automatically used to improve recognition when the caller says that name. No setup is needed beyond passing the variables you already use.
### The agent `sttKeyterms` setting
Set boosted terms once on the agent and they apply to every call:
* In the dashboard: the "Boosted Terms" field in the agent's settings.
* In the API: the `sttKeyterms` array on create or update agent requests.
```json theme={null}
{
"sttKeyterms": ["Xylera", "Acme Corporation", "Krzysztof Wozniak"]
}
```
### The `stt_keyterms` variable
For full control, set the reserved variable `stt_keyterms` to a comma-separated list of terms to boost:
```json theme={null}
{
"variables": {
"first_name": "Theresa",
"last_name": "O'Neill",
"stt_keyterms": "Zantac, Krause and Kinsman, AFFF exposure"
}
}
```
You can set it anywhere variables are set: agent default variables, the create-call request, or your pre-call webhook response, so the boost list can be personalized per call.
Guidelines:
* Keep terms short: single words or short phrases up to about 4 words work best.
* Boost only terms that are likely to be spoken and are hard to recognize (unusual names, product names, case types). Avoid boosting common words.
* Up to 50 terms are applied per call. The agent `sttKeyterms` setting, the `stt_keyterms` variable, and automatic name variables are combined and de-duplicated.
* Like any variable, `stt_keyterms` is also visible to the agent as context, which helps it spell and repeat the terms correctly.
If callers report the agent mishearing a specific name or product, add it to `stt_keyterms` on the agent's default variables and it will apply to every call for that agent.
## Best Practices
Use descriptive variable names like `customer_name` instead of `name` to avoid confusion when debugging transcripts.
System variables like `{{direction}}` and `{{current_time}}` are particularly useful for context-aware prompts that behave differently for inbound vs outbound calls.
Dynamic variables override default variables. If you set a default variable and also pass it dynamically, the dynamic value will be used for that call.
# Telephony Setup
Source: https://docs.revring.ai/platform/telephony
Connect SIP trunks from Twilio, Telnyx, and other providers
SIP trunks are the foundation of your RevRing phone system. They connect your AI agents to the public telephone network, enabling both inbound and outbound calling.
## Understanding SIP Trunks
A SIP (Session Initiation Protocol) trunk is a virtual phone line that connects your RevRing agents to the phone network through a telephony provider like Twilio or Telnyx. Each trunk can handle multiple phone numbers and concurrent calls.
### Key Concepts
* **Proxy Address**: The SIP server endpoint provided by your telephony provider
* **Authentication**: Optional username and password credentials for SIP registration
* **Phone Numbers**: The phone numbers routed through this trunk (must be in E.164 format)
* **Cloud vs. On-Premise**: RevRing supports cloud-based SIP trunks from providers like Twilio and Telnyx
## RevRing SIP Domain
When configuring your telephony provider to send inbound calls to RevRing, use the following SIP URI:
```
sip:sip.revring.ai
```
**SIP Domain:** `sip.revring.ai`
This URI must be configured as the Origination destination in your telephony provider's trunk settings.
## Twilio Elastic SIP Trunking
Twilio Elastic SIP Trunking is recommended for most users due to its reliability, global coverage, and pay-as-you-go pricing.
### Connect Twilio in one step
The fastest way to bring Twilio numbers to RevRing. You give your Account SID and Auth Token, pick the numbers, and RevRing does every step below for you: it creates the Elastic SIP trunk in your Twilio account, points origination at `sip.revring.ai`, creates the termination credential, moves the numbers onto the trunk, and adds the matching trunk here.
1. Open [SIP Trunks](https://dashboard.revring.ai/app/sip-trunks) and select **Connect Twilio**
2. Enter your **Account SID** and **Auth Token** (Twilio Console home page, Account Info) and select **Find my numbers**
3. Pick the numbers to connect. Each one shows where its calls go today, and numbers already on RevRing cannot be picked again
4. Optionally choose the agent that answers these numbers, then select **Connect**
Calls to the connected numbers come to RevRing right away, and your agents can call out from them. Your Twilio credentials are used for this setup only and are not stored. If any step fails, everything created in your Twilio account is removed and your numbers keep their current routing.
Connecting a number moves its calls to RevRing. If the number answers calls somewhere else today (a voice webhook, a TwiML app or another SIP trunk), that setup stops receiving them.
From the API:
```bash theme={null}
# 1. See the account's numbers and where each routes today
curl -X POST https://api.revring.ai/v1/sip-trunks/connect/twilio/numbers \
-H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{ "twilio": { "accountSid": "AC...", "authToken": "..." } }'
# 2. Connect the ones you want, answered by an agent
curl -X POST https://api.revring.ai/v1/sip-trunks/connect/twilio \
-H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{ "twilio": { "accountSid": "AC...", "authToken": "..." }, "numbers": ["+14155550123"], "agentId": "cm..." }'
```
A Twilio API key (`apiKeySid` and `apiKeySecret`) works in place of the Auth Token. To disconnect later, delete the trunk here, then delete the trunk named **RevRing** under Elastic SIP Trunking in the Twilio Console; its numbers go back to the voice settings configured on each number.
To set up the trunk yourself instead, follow the steps below.
### Prerequisites
* Active Twilio account
* At least one phone number purchased from Twilio
* Elastic SIP Trunking enabled in your Twilio Console
### Step 1: Configure Twilio
1. Log into the [Twilio Console](https://console.twilio.com)
2. Navigate to **Elastic SIP Trunking** → **Trunks**
3. Click **Create new SIP Trunk** or select an existing trunk
4. Note your **SIP Domain** (e.g., `yourtrunk.pstn.twilio.com`)
### Step 2: Create Credential List
1. In the Twilio Console, go to **Elastic SIP Trunking** → **Credential Lists**
2. Click **Create new Credential List**
3. Give it a name (e.g., `RevRing Credentials`)
4. Add a credential with a **Username** and **Password** of your choice
5. Save the Credential List
### Step 3: Assign Phone Numbers in Twilio
1. In your trunk settings, go to the **Numbers** tab
2. Click **Add Existing Number**
3. Select the phone numbers you want to use with RevRing
4. Click **Add**
Phone numbers must be assigned to the trunk in Twilio before they can be used in RevRing.
### Step 4: Configure Origination (Inbound)
For inbound calls to reach RevRing:
1. In your trunk settings, go to **Origination**
2. Add an **Origination SIP URI**:
```
sip:sip.revring.ai
```
3. Set **Priority** to `1` and **Weight** to `1`
### Step 5: Configure Termination (Outbound)
1. In your trunk settings, go to **Termination**
2. Note your **Termination SIP URI** (e.g., `yourtrunk.pstn.twilio.com`) - you'll use this as the Proxy in RevRing
3. Under **Authentication** → **Credential Lists**, select the Credential List you created in Step 2
4. Optionally enable **PSTN Transfer** if you want your agents to use the Transfer To Number tool to transfer calls to external phone numbers
### Step 6: Add Trunk to RevRing
1. Navigate to [SIP Trunks](https://dashboard.revring.ai/app/sip-trunks)
2. Click **Add SIP Trunk**
3. Fill in the configuration:
* **Name**: `Twilio Elastic` (or any descriptive name)
* **Proxy**: Your Twilio Termination SIP URI from Step 5 (e.g., `yourtrunk.pstn.twilio.com`) - enter just the domain without the `sip:` prefix
* **Username**: Enter the username from your Credential List (created in Step 2)
* **Password**: Enter the password from your Credential List (created in Step 2)
* **Phone Numbers**: Enter your phone numbers in E.164 format, comma-separated
```
+12025551234, +14155552671, +13105558899
```
4. Click **Add Trunk**
You can paste a list of phone numbers separated by commas, spaces, or newlines - RevRing will automatically parse them.
**Important**: Phone numbers must be assigned in both places:
* In your Twilio Console (assigned to the trunk)
* In RevRing (entered in the Phone Numbers field above)
For **inbound calls** to a specific agent, you must also assign the phone number to that agent in the RevRing dashboard under **Agent → Phone Numbers**. For **outbound calls**, agents can use any phone number from their assigned SIP trunk.
**Processing PHI?** If you're handling protected health information, you must:
1. Enable **Secure Trunking** (TLS/SRTP) in your Twilio trunk settings
2. Contact RevRing support to enable TLS and SRTP on your SIP connection
## Telnyx SIP Trunking
Telnyx offers competitive pricing and global coverage with a developer-friendly API.
### Prerequisites
* Active Telnyx account
* At least one phone number purchased from Telnyx
* SIP trunk created in Telnyx Portal
### Step 1: Configure Telnyx
1. Log into the [Telnyx Portal](https://portal.telnyx.com)
2. Navigate to **Voice** → **SIP Trunks**
3. Click **Create SIP Trunk** or select an existing trunk
4. Note your **SIP Trunk URI** (e.g., `sip.telnyx.com`)
### Step 2: Configure Outbound Settings
1. In your trunk settings, go to **Outbound**
2. Set **Tech Prefix**: Leave empty or as configured
3. Enable **ANI Override** if you need to set custom caller ID
### Step 3: Configure Inbound Settings
1. In your trunk settings, go to **Inbound**
2. Add a **Connection** pointing to RevRing's SIP URI: `sip:sip.revring.ai`
3. Set **Routing** to forward calls to the connection
Optionally enable **PSTN Transfer** in your trunk settings if you want your agents to use the Transfer To Number tool to transfer calls to external phone numbers.
### Step 4: Assign Phone Numbers
1. Navigate to **Phone Numbers** in Telnyx Portal
2. Select the numbers you want to use
3. Set **Connection** to your SIP trunk
4. Save changes
### Step 5: Add Trunk to RevRing
1. Navigate to [SIP Trunks](https://dashboard.revring.ai/app/sip-trunks)
2. Click **Add SIP Trunk**
3. Fill in the configuration:
* **Name**: `Telnyx` (or any descriptive name)
* **Proxy**: `sip.telnyx.com` (or your custom domain) - enter just the domain without the `sip:` prefix
* **Username**: Your Telnyx SIP username (if authentication is enabled)
* **Password**: Your Telnyx SIP password (if authentication is enabled)
* **Phone Numbers**: Enter your phone numbers in E.164 format
4. Click **Add Trunk**
**Important**: Phone numbers must be assigned in both places:
* In your Telnyx Portal (assigned to the trunk)
* In RevRing (entered in the Phone Numbers field above)
For **inbound calls** to a specific agent, you must also assign the phone number to that agent in the RevRing dashboard under **Agent → Phone Numbers**. For **outbound calls**, agents can use any phone number from their assigned SIP trunk.
**Processing PHI?** If you're handling protected health information, you must:
1. Enable **Secure Trunking** (TLS/SRTP) in your Telnyx trunk settings
2. Contact RevRing support to enable TLS and SRTP on your SIP connection
## Managing SIP Trunks
### Editing Phone Numbers
To add or remove phone numbers from an existing trunk:
1. Navigate to [SIP Trunks](https://dashboard.revring.ai/app/sip-trunks)
2. Click the menu (three dots) on the trunk row
3. Select **Edit Numbers**
4. Add or remove phone numbers as needed
5. Click **Save Numbers**
Phone numbers must exist and be properly configured in your telephony provider before adding them to RevRing.
Remember: To enable inbound calls for a specific agent, assign the phone number to that agent under **Agent → Phone Numbers** in the RevRing dashboard.
### Deleting a Trunk
To delete a SIP trunk:
1. Navigate to [SIP Trunks](https://dashboard.revring.ai/app/sip-trunks)
2. Click the menu (three dots) on the trunk row
3. Select **Delete**
4. Confirm the deletion
You cannot delete a SIP trunk that is assigned to active agents. Remove the trunk from all agents first.
## Phone Number Format
All phone numbers in RevRing must use **E.164 format**:
* Start with `+`
* Include country code
* Include area code and number
* No spaces, dashes, or parentheses
### Valid Examples
```
+12025551234 (US number)
+442071234567 (UK number)
+61298765432 (Australia number)
```
### Invalid Examples
```
(202) 555-1234 (contains parentheses and dashes)
2025551234 (missing + and country code)
+1-202-555-1234 (contains dashes)
```
## Troubleshooting
* Verify the proxy address is correct and matches your provider's configuration (should be their Termination SIP URI without the `sip:` prefix)
* Ensure phone numbers are assigned to the trunk in both your provider and RevRing
* Check that your provider's Origination settings include RevRing's SIP URI: `sip:sip.revring.ai`
* Confirm there are no firewall rules blocking SIP traffic
* Verify the phone number is assigned to an agent in the Phone Numbers tab
* Check that the agent status is set to Active
* Ensure your provider is routing calls to RevRing's origination endpoint
* Review call logs in your provider's dashboard for routing errors
* Confirm your trunk has sufficient balance/credit with your provider
* Verify the from number is assigned to your trunk
* Check that the to number is in valid E.164 format
* Review your provider's outbound calling restrictions and country permissions
* If your trunk requires authentication, ensure username and password are correct
* Verify your provider's authentication settings are configured correctly
* Check your provider's authentication logs for specific error messages
## Best Practices
* **Use descriptive names** for your trunks (e.g., "Twilio US Production", "Telnyx Canada")
* **Keep phone numbers updated** in both your provider and RevRing when making changes
* **Monitor trunk capacity** to ensure you have enough concurrent call capacity for your needs
* **Set up redundancy** by configuring multiple trunks or providers for failover
* **Review call logs** regularly to identify and resolve any routing issues
## Next Steps
Configure agents to handle inbound calls
Set up outbound calling through your agents
# Test Suites
Source: https://docs.revring.ai/platform/test-suites
Automatically test your AI agents with scripted conversations and AI-powered scoring
Test suites let you automatically validate your agents by running scripted test conversations and scoring the results. Define test cases with a script (what the tester should say) and a scoring rubric (what counts as a pass), then run them at scale to catch regressions and verify agent behavior.
## Overview
A test suite consists of:
* **Target agent** — the agent being tested
* **Tester** — either the built-in **system tester** (simpler, requires no setup) or a **custom agent** you've created that role-plays as the caller
* **Test cases** — individual scenarios, each with a script and scoring rubric
* **Test runs** — executions of the full suite, producing pass/fail results for each case
Each suite has a **test type** that controls how runs execute:
| Type | What happens | Best for |
| - | - | - |
| **Chat** (default) | The tester and your agent hold a fast text conversation. Your agent uses its real prompt, knowledge bases, and tools — same brain, no audio. Runs finish in seconds. | Everyday iteration: build, test, fix, rerun |
| **Voice** | A full simulated phone call between the tester and your agent. | Final validation of the live call experience before launch |
| **SMS** | The tester texts your agent, and replies are produced by the same engine that answers real inbound texts: the agent's SMS prompt (or its main prompt if none is set), knowledge bases, and tools. | Validating your agent's texting behavior before pointing real traffic at it |
| **Web chat** | The tester chats with your agent the way a website visitor would, using the agent's web chat prompt (or its main prompt if none is set), knowledge bases, and tools. | Validating the website chat experience, including page-aware behavior via a `page_context` variable |
In chat tests, phone-only actions are simulated so the whole flow can be scored: when your agent transfers the caller, the transfer succeeds in the simulation, so rubrics can verify the agent transferred to the right number under the right conditions. A chat-type suite can still run a one-off voice test by passing `mode: "voice"` when starting a run.
In voice tests, transfers are simulated the same way: the test call is isolated by design, so instead of dialing a live phone line the transfer completes in simulation and rubrics score the agent's transfer behavior normally. See [Simulating Transfer Outcomes](#simulating-transfer-outcomes) for testing the failure path.
SMS and web chat tests exercise your agent's real messaging behavior end to end, with two safety guarantees: no actual message is ever sent to a phone number or visitor, and nothing is written to your Conversations. Custom tools do run for real, and their webhook calls are marked with channel `"test"` (plus the test call ID) so your integrations can tell test traffic apart. A test's conversation lives on the test attempt, exactly like every other test type.
After each conversation, the transcript is evaluated against the scoring rubric to produce a pass, fail, or error result with reasoning.
Test conversations run until they end naturally: the tester finishes its script, or your agent ends the call. There is no practical length limit — long intake cases with dozens of exchanges run to completion. (An extreme safeguard of several thousand messages exists purely to stop a misconfigured never-ending conversation, far beyond any realistic test.)
Test runs are billed like regular usage at your listed per-minute rates, based on conversation length. Chat tests are typically much shorter — and therefore cheaper — than voice tests.
## Simulating Transfer Outcomes
Transfers complete in simulation on every test call, in both chat and voice mode, so transfer cases score the agent's behavior rather than the test environment. By default the simulated transfer succeeds.
To test how an agent handles a transfer that does not go through (for example a take-a-message flow), set the test variable `_sim_transfer` to `fail` on the case. In the dashboard this is the "Simulate a failed transfer in this test" checkbox on the test case. Via the API, set it in the case's `variables`:
```json theme={null}
POST /v1/test-suites/{suiteId}/tests
{
"name": "Failed transfer takes a message",
"script": "Ask to speak with a person. When offered a message instead, accept and provide your details.",
"scoringRubric": "PASS only if the agent offers to take a message after the failed transfer and collects the caller's details.",
"variables": { "_sim_transfer": "fail" }
}
```
With the variable set, the simulated transfer fails and the agent follows its failure instructions, so rubrics can score that path. Real calls are never affected by this variable.
## Suggest a Fix
When a test fails, you can ask for a suggested prompt change that would make it pass. The suggestion includes an explanation of the failure, a plain-language summary of what changed, the complete corrected prompt ready to save to the agent, and any problems found in the test's own pass criteria. In the dashboard, open a failed test result and choose **Suggest a fix**.
Preparing a suggestion takes time, particularly for agents with long prompts, so over the API it works as a short-lived job:
`POST /v1/test-suites/{id}/attempts/{attemptId}/suggest-fix?wait=0` returns immediately with `status: "running"`.
`GET` the same URL every couple of seconds. `status` stays `running` while it is being prepared, then becomes `ready` with the suggestion in `result`.
Save `result.suggestedPrompt` to the agent with the usual agent update call, then rerun the suite to confirm the fix.
Because the request never waits for preparation, this pattern is unaffected by how long a suggestion takes. If you would rather not poll, omit `wait=0` and the request waits up to 105 seconds and returns the suggestion directly when it is ready in time; if your client gives up first, simply repeat the request and the finished suggestion comes back immediately.
### One Suggestion for a Whole Run
When several tests fail in the same run, requesting a fix per attempt produces separate prompts that do not know about each other, and applying one can undo another. Instead, request one suggestion for the run:
```
POST /v1/test-suites/{id}/runs/{runId}/suggest-fix
GET /v1/test-suites/{id}/runs/{runId}/suggest-fix
```
This analyzes every failed attempt in the run together and returns a single corrected prompt that addresses all of them at once, so failures that share a root cause get one coherent edit. The job, polling, and `wait=0` semantics are identical to the per-attempt endpoint, and the result has the same fields plus:
* `result.perAttempt` — one entry per failed attempt (`attemptId`, `testCaseName`, `analysis`) explaining why that test failed and which part of the edit addresses it.
* `result.skippedAttemptIds` — present only on very large runs: the run's oldest failures beyond the most recent 12, which were not analyzed.
The run must be finished before requesting a suggestion, and it must contain at least one failed attempt.
### Knowing When a Suggestion Is Out of Date
Every `ready` response includes a `stale` boolean next to `status` and `result`. It is `false` when the agent's prompt is still the one the suggestion was written against, and `true` if the prompt has changed since, in which case saving `result.suggestedPrompt` would overwrite those newer edits. When `stale` is `true`, request a fresh suggestion instead of applying the old one. This applies to both the per-attempt and the run-level endpoints.
## Creating a Test Suite
### From the Dashboard
1. Navigate to **Test Suites** in the sidebar
2. Click **New Test Suite**
3. Enter a **Name**
4. Select the **Target Agent** to test
5. Choose the **Test Type**: **Chat** (default) for fast text conversations, **Voice** for full simulated calls, or **SMS** / **Web chat** to test the agent's messaging channels
6. Choose a tester mode:
* **Use system tester** — simpler, no setup required. RevRing's built-in tester follows each test script automatically.
* **Use custom agent as tester** — select one of your own agents to play the caller role. Useful when you need the tester to have specific voice, language, or behavioral settings.
7. Set **Max Concurrency** to control how many test calls run in parallel (min 2, max 100, default 2). Applies to voice tests, where each test requires two concurrent calls — one outbound from the tester and one inbound to the target.
8. Click **Create**
**Default Variables** can be added after creation by editing the test suite. Open the suite and click **Edit** to set variables that apply to all test calls.
### Via API
```bash theme={null}
curl -X POST https://api.revring.ai/v1/test-suites \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer Support Regression Tests",
"targetAgentId": "agent_target_id",
"testMode": "chat",
"testerAgentId": "agent_tester_id",
"maxConcurrency": 5,
"defaultVariables": {
"customer_name": "Test Customer",
"account_id": "TEST-001"
}
}'
```
To list suites, optionally filtered to one agent (useful when older agents' suites would otherwise fall outside the first page):
```bash theme={null}
curl "https://api.revring.ai/v1/test-suites?targetAgentId=agent_target_id&page=1&pageSize=25" \
-H "x-api-key: YOUR_API_KEY"
```
When starting a run you can override the suite's test type for that run only, for example a final voice validation on a chat suite: pass `{ "mode": "voice" }` to the run endpoint. The same override accepts `"sms"` and `"web_chat"`, so one suite of scenarios can be exercised across channels without duplicating test cases.
SMS and web chat tests require the agent to have a usable text prompt: its channel-specific prompt if you have set one, otherwise its main prompt. Starting a run for an agent with neither returns a `channel_prompt_missing` error.
## Writing Test Cases
Each test case has three key fields:
| Field | Description |
| - | - |
| **Name** | A descriptive name for the test (e.g., "Handles refund request correctly") |
| **Script** | Instructions for the tester agent — what to say, what scenario to simulate |
| **Scoring Rubric** | Criteria for evaluating the target agent's performance — what constitutes a pass or fail |
### Script
The script tells the tester agent how to behave during the call. Write it as natural language instructions:
```
You are calling a customer support line to request a refund for order #12345.
1. When the agent greets you, say you'd like a refund for your recent order.
2. Provide the order number #12345 when asked.
3. Say the reason is that the product arrived damaged.
4. If the agent offers a replacement instead, insist on a refund.
5. Accept the refund if offered and say thank you.
```
### Scoring Rubric
The scoring rubric defines what a successful interaction looks like. After the call, the transcript is evaluated against these criteria:
```
PASS if ALL of the following are true:
- The agent acknowledged the refund request
- The agent asked for the order number
- The agent processed or offered to process the refund
- The agent was polite and professional throughout
FAIL if ANY of the following are true:
- The agent refused the refund without explanation
- The agent provided incorrect information
- The agent was rude or dismissive
- The agent failed to ask for the order number
```
### Creating Test Cases
**Dashboard:** Open your test suite, go to the **Configure Tests** tab, click **Add Test**, and fill in the name, script, and scoring rubric.
**API:**
```bash theme={null}
curl -X POST https://api.revring.ai/v1/test-suites/{suiteId}/tests \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Refund request - damaged item",
"script": "You are calling to request a refund for order #12345 because the product arrived damaged...",
"scoringRubric": "PASS if the agent acknowledges the request and processes the refund. FAIL if the agent refuses without reason.",
"attemptsPerRun": 1
}'
```
The `attemptsPerRun` field (1–10, default 1) controls how many times each test case is executed per run. Use multiple attempts to test for consistency.
### AI-Generated Test Cases
You can also generate test cases automatically using AI. Provide a prompt describing the scenarios you want to test, and RevRing generates test cases based on your target agent's configuration:
**Dashboard:** Click **Generate Tests** in your test suite and enter a prompt describing the scenarios you want. RevRing generates test cases based on your target agent's configuration. You can refine the results with follow-up messages — the generator keeps the conversation context so you can ask for adjustments or additional scenarios.
Each generated test can be individually **added** to your suite or **dismissed**. Review each one before adding to make sure the script and rubric match your expectations.
**API:**
```bash theme={null}
curl -X POST https://api.revring.ai/v1/test-suites/{suiteId}/generate-tests \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Generate test cases for edge cases like the caller being angry, asking to speak to a manager, or providing an invalid order number."
}'
```
The response includes generated test cases with `name`, `script`, and `scoringRubric` fields that you can review, edit, and save.
### From a Real Call
Turn any call into a test: open the call and select **Create test**, or call the API. The caller's side of the call becomes the test script (what they said, in order, with the turns that led to the problem) and what should have happened becomes the scoring rubric. If the call failed any of the agent's [success criteria](/platform/webhooks#post-call-evaluation), the test focuses on those; otherwise give a `focus`. The call's own variables are replayed.
```bash theme={null}
curl -X POST https://api.revring.ai/v1/test-suites/{suiteId}/tests/from-call \
-H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{ "callId": "a1b2c3d4-...", "focus": "The agent should offer the earliest available slot." }'
```
Pass `"save": false` to get the drafted test back without saving it. A test built from a failed call is the fastest way to prove a fix: run it, apply the change (or [Suggest a fix](#suggest-a-fix)), and run it again.
### Fix Failing Calls
When an agent's calls start failing a [success criterion](/platform/webhooks#post-call-evaluation), build a test suite from those calls in one step. On the dashboard Overview, pick the agent and select **Fix** next to the criterion, or:
```bash theme={null}
curl -X POST https://api.revring.ai/v1/test-suites/from-failing-calls \
-H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{ "agentId": "cm...", "criterion": "booked_appointment", "days": 7, "limit": 10 }'
```
The most recent failing calls (up to `limit`, within `days`) each become a test in a new suite, and a run starts right away. Then use [Suggest a fix for the whole run](#one-suggestion-for-a-whole-run) to get one change that addresses every case, apply it, and run the suite again to confirm.
## Suggest a fix
When a test attempt fails, open it and click **Suggest a fix**. RevRing reviews the conversation, the test script, the pass criteria, and your agent's prompt, then proposes a minimal prompt update with a clear explanation and a line-by-line view of exactly what would change. If the test's pass criteria themselves look unachievable (for example, requiring an outcome outside the agent's control), that is flagged too, so you can fix the test instead of the agent. **Apply and rerun** applies the update and starts a fresh run in one step — with chat tests, you'll see the result in seconds.
## Running Tests
### Starting a Test Run
**Dashboard:** Go to the **Runs** tab and click **Run Tests**. Enter a run name (or accept the auto-generated timestamp name) and click **Start**. You can monitor progress in real time.
**API:**
```bash theme={null}
curl -X POST https://api.revring.ai/v1/test-suites/{suiteId}/runs \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "v2.1 regression check"
}'
```
### Test Run Lifecycle
| Status | Description |
| - | - |
| `queued` | Run is queued and waiting to start |
| `running` | Test calls are being placed and scored |
| `completed` | All test cases have been evaluated |
| `failed` | The run encountered an error |
| `cancelled` | The run was manually cancelled |
### Monitoring Progress
Each test run tracks summary statistics:
* **Total Tests** — total number of test attempts
* **Passed** — number of attempts that passed
* **Failed** — number of attempts that failed
**Dashboard:** The test run detail page shows real-time progress with results for each attempt. Click **View Call Details** on any attempt to see the full call record.
**API:**
```bash theme={null}
curl https://api.revring.ai/v1/test-suites/{suiteId}/runs/{runId} \
-H "x-api-key: YOUR_API_KEY"
```
### Test Attempt Results
Each test attempt produces:
| Field | Description |
| - | - |
| `result` | `pass`, `fail`, or `error` |
| `reasoning` | AI-generated explanation of why the test passed or failed |
| `transcript` | The full conversation transcript |
| `callId` | Link to the outbound call record |
| `inboundCallId` | Link to the inbound call record (on the target agent) |
### Exporting Results
After a run completes, you can export the results as JSON. Filter by **pass**, **fail**, or **error** before exporting to narrow down the data. Results can be copied to clipboard or downloaded as a file.
### Cancelling a Run
**Dashboard:** Click **Stop** on a running test run.
**API:**
```bash theme={null}
curl -X POST https://api.revring.ai/v1/test-suites/{suiteId}/runs/{runId}/cancel \
-H "x-api-key: YOUR_API_KEY"
```
Only `queued` or `running` test runs can be cancelled.
## Test Variables
Variables can be set at multiple levels. More specific values override broader ones:
1. **Default variables** on the test suite — apply to all test calls
2. **Per-test-case variables** — override suite defaults for a specific test
3. **Run variables** — passed when starting a run, override both for that run only
During a test they are merged over the agent's own default variables, your pre-call webhook still runs as well (its payload includes `isTestCall: true`), and the prompt renders exactly as it would on a real call.
Per-test-case variables are useful for testing different caller scenarios:
```json theme={null}
{
"name": "VIP customer refund",
"script": "Call and ask for a refund...",
"scoringRubric": "PASS if the agent offers expedited refund for VIP...",
"variables": {
"customer_name": "VIP Customer",
"account_status": "premium"
}
}
```
Run variables let you supply values at the moment you start a run, without editing the suite or its tests — for example when the values come from your own application:
```bash theme={null}
POST /v1/test-suites/{id}/runs
{
"variables": {
"customer_name": "Jordan Alvarez",
"claim_type": "auto"
}
}
```
### Seeing What a Run Ran With
Every attempt records the variables it ran with (the merged result of suite defaults, test case variables, and run variables) in its `variables` field, visible on the run's attempts via `GET /v1/test-suites/{id}/runs/{runId}`. The snapshot is taken when the run starts, so it stays inspectable even after the suite or its tests are edited. Attempts from before this field existed show `null`.
## Best Practices
### Writing Effective Scripts
* Be specific about what the tester should say and do
* Include numbered steps for multi-turn conversations
* Specify how the tester should respond to common agent behaviors
* Keep scripts focused on one scenario per test case
### Writing Effective Rubrics
* Use clear PASS/FAIL criteria
* Be specific about what constitutes success vs. failure
* Include both positive (must happen) and negative (must not happen) criteria
* Account for acceptable variations in agent responses
### Test Organization
* Group related tests in the same suite (e.g., "Refund Scenarios", "Appointment Booking")
* Use descriptive names so test results are easy to understand at a glance
* Start with key happy-path scenarios, then add edge cases
* Run tests after every agent prompt change to catch regressions
### Concurrency
The `maxConcurrency` setting controls how many test calls run simultaneously (minimum 2, maximum 100). Higher concurrency completes runs faster but counts against your organization's call concurrency limit. Start with a low value (2–5) and increase as needed.
## Troubleshooting
An `error` result means the test call itself failed (e.g., connection issue, agent not reachable). This is different from a `fail`, which means the call completed but didn't meet the rubric criteria. Check the call logs for the specific error message.
Increase `maxConcurrency` on the test suite to run more calls in parallel. Note that concurrent test calls count against your organization's overall concurrency limit.
Review the scoring rubric for ambiguous criteria. The AI evaluator takes the rubric literally — vague rubrics produce inconsistent results. Use multiple attempts per run (`attemptsPerRun`) to identify inconsistency.
If using a custom tester agent, ensure its prompt instructs it to follow the test script closely. The script is injected into the tester agent's context, but a conflicting system prompt may override it. If you don't need special tester behavior, consider switching to the **system tester** instead.
This means the tester and target agent are sharing the same SIP trunk phone numbers. Each test call requires two participants on separate lines — the tester calls the target. Add another phone number to your SIP trunk so the tester can call from a different number than the target receives on.
## Next Steps
Learn about outbound calling and concurrency
Manage test suites programmatically
Build structured conversation flows to test
Configure agent tools and capabilities
# Agent Tools
Source: https://docs.revring.ai/platform/tools
Configure system and custom tools for your agents
Tools enable your AI agents to take actions during phone calls beyond just conversation. RevRing provides built-in system tools and supports custom tools via webhooks.
## System Tools
System tools are pre-built capabilities that agents can invoke during calls. These tools are configured in the **Tools** tab of your agent settings.
**Flow agents**: End Call, Transfer, and Press Digits are handled by dedicated [flow nodes](/platform/conversation-flows) instead of system tools. These three tools only appear on the Tools tab for single-prompt agents. Voicemail Detection and custom tools are available for both modes.
### End Call
The End Call tool allows agents to automatically terminate calls based on configurable rules.
#### When to Use
* Calls that have reached their natural conclusion
* Customer explicitly ends the conversation
* Maximum conversation time is reached
* Call objective has been completed
#### Configuration
1. Navigate to the **Tools** tab
2. Locate **End Call** under System Tools
3. Toggle the switch to enable
4. Click **Configure** to customize behavior
5. Set **End Call Rules Override** to define when the agent should end calls:
```
End the call when:
- The customer says goodbye or indicates they're done
- The appointment has been confirmed
- The customer explicitly asks to end the call
- You've provided all requested information and there are no further questions
- The conversation has exceeded 5 minutes
```
6. Click **Save**
If you leave the rules override empty, RevRing uses sensible defaults. Custom rules give you more control over call termination behavior.
#### How It Works
When enabled, the agent has access to an `end_call` function. During the conversation, if the agent determines (based on the rules) that the call should end, it will:
1. Invoke the `end_call` function
2. Speak a brief closing message
3. Terminate the call gracefully
You can see end call invocations in the call transcript labeled as "Tool: end\_call".
### Transfer
The Transfer tool enables agents to transfer calls to other phone numbers or departments based on conditions you define. RevRing supports two transfer modes: **cold** and **warm**.
#### When to Use
* Customer requests to speak with a human representative
* Issue requires specialized expertise beyond the agent's scope
* Escalation to management or specific departments
* Routing to on-call support staff
#### Configuration
1. Navigate to the **Tools** tab
2. Locate **Transfer** under System Tools
3. Toggle the switch to enable
4. Click **Configure** to set up transfer routes
5. Select the **Default Transfer Mode** (applied to any route that doesn't set its own mode):
* **Cold Transfer** — the agent transfers the call immediately and disconnects
* **Warm Transfer** — the agent places the caller on hold, calls the recipient to brief them, then connects the parties
6. Add transfer routes:
**Transfer Routes**:
* **To number**: `+12025559999` (destination phone number in E.164 format)
* **Extension** (optional): touch-tone digits dialed after the call connects, to reach an extension behind a phone menu or auto-attendant. Allowed characters: `0-9`, `*`, `#`, and `w` for a 0.5-second pause. Start with `wwww` (a 2-second pause) so the destination is ready for input, e.g. `wwww1234` — see [Reaching an extension](#warm-transfer) below. Sent on warm transfers only.
* **Condition**: `Transfer to sales team for pricing questions or to close deals`
* **Mode**: `Default`, `Cold`, or `Warm` — overrides the default transfer mode for this specific route
Click **Add route** to add multiple destinations.
**Transfer Description Override** (optional): Custom instructions for when the agent should offer a transfer.
7. Click **Save**
You can configure multiple transfer routes for different scenarios. The agent will choose the appropriate route based on the conversation context and conditions you define.
**Fallback chains**: Because each route can set its own mode, you can build a ring tree. For example, a warm transfer to a primary number, a warm transfer to a backup if the first isn't answered, and a final cold transfer as a last resort. When a warm transfer isn't answered, the agent continues to the next route based on your prompt and conditions. (Cold transfers hand off immediately and cannot fall back, so they belong last.)
#### Cold Transfer
In cold transfer mode, the agent invokes the `transfer_to_number` function and the call is immediately handed off to the destination number. RevRing's involvement ends — billing stops and recording stops once the transfer is initiated.
The transfer appears in the call transcript as a `transfer_to_number` tool invocation.
**Provider Setup Required**: Ensure your SIP trunk provider supports SIP REFER and has call transfers enabled. Some providers like Twilio require enabling **PSTN Transfer** in your trunk settings. See the [Telephony Setup](/platform/telephony) guide for details.
#### Warm Transfer
Warm transfer provides a more seamless handoff. When the agent decides to transfer:
1. The **caller is placed on hold** — they hear a hold message and optional hold music
2. The **agent calls the recipient** at the transfer destination number
3. The agent **briefs the recipient** with a summary of the conversation so far
4. The **caller is connected** to the recipient
Warm transfer is configured with the following settings:
| Setting | Description |
| - | - |
| **Hold Message** | Message spoken to the caller while on hold (e.g., "Please hold while I transfer you.") Max 500 characters. |
| **Hold Music** | Enable or disable hold music while the caller waits |
| **Summary Instructions** | Extra guidance for the AI when briefing the human agent. Max 2,000 characters. If omitted, a default summary is generated. |
| **Intro Message** | Spoken after the human agent accepts, heard by both parties (e.g., "You're now connected. They're up to speed on your request.") Max 500 characters. |
| **Continue Recording After Handoff** | Keep recording after the agent hands off, so the recording also includes the human agent and caller conversation. |
| **Caller ID Shown to Recipient** | Which number the transfer recipient sees. `agent` (default) shows the agent's own number for the call. `original_caller` shows the customer's number, so the recipient sees who is on the line. |
| **No Answer Timeout** | How long the destination may ring before the attempt is abandoned. 10 to 300 seconds, default 75. |
| **Wait for a Person** | How long to wait after the destination answers for a person to actually come on the line. Covers queues and hold systems that pick up but never hand over. 15 to 600 seconds, default 300. |
**Presenting the original caller's number**: a warm transfer places a new outbound call to the recipient over your own trunk, so `callerIdMode: "original_caller"` sends a caller ID your account does not own. Support for this is set by your telephony provider, not by RevRing. Many providers allow it, often as a per-trunk option named something like ANI override or caller ID passthrough, and some require it to be enabled before it takes effect. Others only permit caller IDs you own or have verified in advance. If the caller withheld their number, or it is not a usable phone number, the agent's number is presented instead. If your provider only permits owned or verified caller IDs, the provider refuses the dial and the transfer attempt fails, so confirm support with your provider before enabling this option on a live agent.
A cold transfer is handed off by your provider rather than re-dialed, so the caller ID it presents is governed by your trunk's own transfer settings.
**Reaching an extension**: If a route has an **Extension**, the agent dials it as touch tones the moment the recipient's line answers — sooner than any person could dial by hand. Many phone menus and auto-attendants are not ready for input in that first instant, and digits that arrive too early are silently ignored, leaving the call wherever the line answered (typically the main menu or front desk).
To avoid this, begin the extension with `w` pauses. Each `w` waits 0.5 seconds before the digits are dialed, and we recommend starting with `wwww` (2 seconds), which works with nearly all phone systems:
* `wwww1234` — wait 2 seconds, then dial `1234`
* `wwww1ww8011` — pauses and digits can be mixed, e.g. to navigate a menu before an extension
If the destination still doesn't pick up the digits, add more `w`s. Extensions are dialed on warm transfers only, because a cold transfer hands the call off immediately.
#### Configuring via API
Set `transferMode` (the default mode) and `warmTransferConfig` when creating or updating an agent. Each route may include an optional `mode` (`"cold"` or `"warm"`) that overrides the default for that route; omit it to inherit `transferMode`. The example below builds a warm primary, warm backup, then cold last-resort fallback chain:
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/{agentId} \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transferEnabled": true,
"transferMode": "warm",
"transferRoutes": [
{
"to_number": "+12025559999",
"condition": "Primary: transfer to the support team",
"mode": "warm",
"extension": "123#"
},
{
"to_number": "+12025550000",
"condition": "Backup: use if the primary line is not answered",
"mode": "warm"
},
{
"to_number": "+12025551111",
"condition": "Last resort: drop to the main line if no one is available",
"mode": "cold"
}
],
"warmTransferConfig": {
"holdMessage": "Please hold while I connect you with our support team.",
"holdMusicEnabled": true,
"summaryPrompt": "Focus on the customer's main concern and any account details mentioned.",
"introMessage": "You're now connected. They're up to speed on your request.",
"continueRecordingAfterTransfer": false,
"dialTimeoutSeconds": 75,
"waitForHumanSeconds": 300
}
}'
```
**If nobody answers**: the agent lets the destination ring for `dialTimeoutSeconds` seconds (10 to 300, default 75), then tells the caller it could not connect and continues the conversation. The caller can ask to try again, or the conversation can fall through to another route. Raise the timeout for teams that take longer to pick up; lower it to fail over to a backup route faster.
**If the line answers but nobody comes on**: some destinations pick up into a queue, a hold system, or an unattended handset. The agent waits `waitForHumanSeconds` (15 to 600, default 300) for a person to speak. If nobody does, it ends the attempt, releases that leg, tells the caller the destination answered but no one came on the line, and continues the conversation. Raise it for queues that routinely hold callers for several minutes.
Both timeouts can also be set per route, which is useful when one agent transfers to both a fast internal desk and a slow queue line:
```json theme={null}
"transferRoutes": [
{ "to_number": "+12025550123", "condition": "Billing questions", "mode": "warm", "dialTimeoutSeconds": 30 },
{ "to_number": "+12025550199", "condition": "Claims queue", "mode": "warm", "dialTimeoutSeconds": 120, "waitForHumanSeconds": 300 }
]
```
Warm transfer is also available as a node type in [Conversation Flows](/platform/conversation-flows), where you can configure hold and summary settings per-node.
### Voicemail Detection
Voicemail Detection automatically identifies when an outbound call reaches an answering machine or voicemail system.
#### When to Use
* Outbound calling campaigns where live conversations are preferred
* Appointment reminders or confirmations
* Sales or follow-up calls
* Any scenario where leaving a message is optional
#### Configuration
1. Navigate to the **Tools** tab
2. Locate **Voicemail Detection** under System Tools
3. Toggle the switch to enable
4. Click **Configure** to set behavior
5. Choose **Voicemail Action**:
* **Hangup**: Immediately end the call when voicemail is detected (saves costs)
* **Leave Message**: Speak a pre-recorded message before hanging up
6. If you selected "Leave Message", configure **Voicemail Message**:
```
Hi, this is a message from Acme Corp. We tried reaching you regarding your appointment. Please call us back at 1-800-555-0123. Thank you.
```
7. Click **Save**
Voicemail detection is most effective for outbound calls. For inbound calls, it's typically not needed since customers are calling you.
#### How It Works
When an outbound call connects, RevRing analyzes the audio to detect voicemail systems. If detected:
* **Hangup action**: Call ends immediately, marked as "voicemail" in logs
* **Leave message action**: Agent speaks the configured message, then hangs up
Detection accuracy is typically very high but not perfect. Review call logs to monitor detection performance.
### Press Digits (DTMF)
The Press Digits tool allows the agent to send DTMF touch tones during a call. This is useful when navigating IVR (interactive voice response) systems — for example, when the agent needs to press digits to navigate a phone menu during a transferred call or an outbound call to a phone system.
#### When to Use
* Navigating automated phone menus (e.g., "Press 1 for sales, press 2 for support")
* Entering account numbers or PINs via keypad
* Interacting with external IVR systems during outbound calls
#### Configuration
1. Navigate to the **Tools** tab
2. Locate **Press Digits** under System Tools
3. Toggle the switch to enable
4. Optionally set a **Custom Instructions Override** to customize when the agent uses this tool
5. Click **Save**
#### Configuring via API
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/{agentId} \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"playKeypadTouchToneEnabled": true,
"playKeypadTouchToneDescriptionOverride": "Use this to press digits when navigating phone menus."
}'
```
[Conversation Flows](/platform/conversation-flows) can also press digits with the `press_digit` node, which navigates a phone menu on its own: it listens to the menu and decides which digit to press.
## Advanced Settings
### Block All Interruptions
By default, callers can interrupt the agent while it's speaking. Enable **Block All Interruptions** to make the agent always finish speaking before listening for input.
#### When to Use
* Delivering important legal disclaimers or disclosures that must be heard in full
* Reading back critical information (order numbers, confirmation codes)
* Playing scripted messages that should not be cut short
#### Configuration
1. Navigate to the **Advanced** tab
2. Toggle **Block All Interruptions** to on
3. Click **Save**
#### Configuring via API
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/{agentId} \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"disableInterruptions": true
}'
```
Block All Interruptions applies to all agent speech globally. In [Conversation Flows](/platform/conversation-flows), you can set `blockInterruptions` on individual nodes for more granular control.
### Ignore Brief Acknowledgments
Callers naturally say things like "okay", "yeah", or "uh-huh" while listening. By default, these brief acknowledgments do not interrupt the agent, so it finishes its sentence smoothly instead of stopping mid-thought. Anything beyond a brief acknowledgment, such as "wait", "stop", a question, or "okay but wait", interrupts the agent immediately. When the agent is not speaking, acknowledgment words behave like any other reply.
This is enabled by default with a recommended list that covers common acknowledgments and adapts to the agent's language setting (for example, Spanish agents also recognize "ajá" and "claro"). You can customize it per agent:
* **Custom word list**: replace the recommended list with your own words or short phrases, for example to cover another language.
* **Disable**: turn the toggle off (or set `interruptionIgnoreWords` to `[]` via API) so every caller utterance can interrupt.
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/{agentId} \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"interruptionIgnoreWords": ["okay", "yeah", "got it", "claro"]
}'
```
### Interruption Word Count
Controls how many words a caller must say before the agent stops talking. The default of 1 keeps the agent responsive to any real interjection. Raise it to make the agent harder to interrupt, or set it to 0 to stop the agent on any caller sound, including background noise.
Urgent phrases such as "stop", "wait", and "hold on" always interrupt the agent immediately, regardless of this setting.
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/{agentId} \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"minInterruptionWords": 2
}'
```
### Interruption Threshold
The **Interruption Threshold** controls how much speech is required from the caller before the agent treats it as an interruption and stops talking. This is measured in milliseconds.
* **Lower values** (e.g. 100 ms) make the agent very sensitive — even a short utterance will interrupt it.
* **Higher values** (e.g. 2000 ms) make the agent less sensitive — the caller must speak for longer before the agent pauses.
* **Default**: 500 ms
This is useful for tuning the conversational feel. If your agent is being interrupted too easily by background noise or brief acknowledgements (like "uh huh"), increase the threshold. If callers have difficulty getting a word in, lower it.
#### Configuration
1. Navigate to the **Advanced** tab
2. Adjust the **Interruption Threshold** slider (100–2000 ms)
3. Click **Save**
#### Configuring via API
```bash theme={null}
curl -X PATCH https://api.revring.ai/v1/agents/{agentId} \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"interruptionThresholdMs": 750
}'
```
If **Block All Interruptions** is enabled, the interruption threshold has no effect since all interruptions are disabled.
## Live Appointment Booking with Cal.com
RevRing hosts a Cal.com connector so agents can check real availability and book appointments live on a call, with no backend required. Two custom tools point at the hosted endpoints and carry your Cal.com API key in server side header configuration, where the model never sees it.
Register these two tools on the agent (dashboard Custom Tools, or `POST /v1/agents/{agentId}/custom-tools`), replacing the key, event type id, and timezone:
```json theme={null}
{
"name": "Check Availability",
"webhookUrl": "https://api.revring.ai/public/integrations/calcom/check-availability?eventTypeId=YOUR_EVENT_TYPE_ID&timeZone=America/New_York",
"enabled": true,
"headers": [{ "key": "x-cal-api-key", "value": "YOUR_CAL_API_KEY" }],
"schema": {
"type": "function",
"name": "check_availability",
"description": "Check the calendar for open appointment times. Use when the caller asks about available times or wants to schedule.",
"parameters": { "type": "object", "properties": {
"date": { "type": "string", "description": "Date to check availability (YYYY-MM-DD). Defaults to today." },
"days_ahead": { "type": "number", "description": "Days ahead to check. Default 2. Max 5." } } }
}
}
```
```json theme={null}
{
"name": "Book Appointment",
"webhookUrl": "https://api.revring.ai/public/integrations/calcom/book-appointment?eventTypeId=YOUR_EVENT_TYPE_ID&timeZone=America/New_York",
"enabled": true,
"headers": [{ "key": "x-cal-api-key", "value": "YOUR_CAL_API_KEY" }],
"schema": {
"type": "function",
"name": "book_appointment",
"description": "Book an appointment on the calendar. Only use after the caller has confirmed a specific date and time.",
"parameters": { "type": "object", "required": ["date", "time"], "properties": {
"date": { "type": "string", "description": "Appointment date YYYY-MM-DD." },
"time": { "type": "string", "description": "Time in HH:MM 24-hour format." },
"first_name": { "type": "string", "description": "Caller's first name." },
"last_name": { "type": "string", "description": "Caller's last name." },
"email": { "type": "string", "description": "Caller's email, required for the calendar invite." },
"phone": { "type": "string", "description": "Caller's phone number, if known." } } }
}
}
```
Then add booking behavior to the prompt: check availability before offering times, offer only times the tool returned, collect the email (and phone when the calendar requires it), book only after the caller confirms, and confirm only after the tool reports success. Have the agent speak a short bridge line ("Let me check what is open...") before each tool call so the lookup never sounds like dead air.
The tools return short spoken ready text, including clear guidance on failures (slot taken, phone number required), so the agent recovers naturally. Phone numbers are normalized automatically, and both Cal.com response formats are handled. If you prefer to run the endpoints on your own backend for custom logic, point the same tools at your URLs instead and keep the key server side.
## Custom Tools
Custom tools allow you to extend agent capabilities by integrating with your own systems via webhooks. When the agent invokes a custom tool, RevRing calls your webhook with the tool's arguments and includes the response in the conversation.
### Use Cases
* **CRM lookups**: Fetch customer information during calls
* **Order status**: Check order or shipment status
* **Appointment booking**: Create or modify appointments in your calendar system
* **Payment processing**: Initiate payments or check account balances
* **Inventory checks**: Verify product availability
* **Custom business logic**: Any operation specific to your workflow
### Creating a Custom Tool
1. Navigate to the **Tools** tab
2. Scroll to the **Custom Tools** section
3. Enter a **Friendly Name** for your tool (e.g., "Order Status Checker")
4. Enter your **Webhook URL** (e.g., `https://api.yourcompany.com/tools/check-order`)
5. Click **Add Tool**
The tool will appear in the table below. Click **Edit** to define the tool's schema.
The **Friendly Name** is just a label for the dashboard. The actual function name that the agent uses is defined in the `name` field of your JSON schema (e.g., `check_order_status`).
### Defining the Tool Schema
The schema defines what parameters the tool accepts using OpenAI's function calling format:
1. Click **Edit** on your custom tool
2. In the **Schema JSON** field, enter your tool definition:
```json theme={null}
{
"type": "function",
"name": "check_order_status",
"description": "Check the status of a customer's order by order ID",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The unique order ID (e.g., ORD-12345)"
}
},
"required": ["order_id"],
"additionalProperties": false
}
}
```
3. Toggle **Enabled** to activate the tool
4. Click **Save**
The `description` fields are critical - they tell the agent when and how to use the tool. Be clear and specific.
### Schema Format Reference
Follow the [OpenAI function calling format](https://platform.openai.com/docs/guides/function-calling#defining-functions):
* **type**: Always `"function"` at the root level (required)
* **name**: Unique identifier for the tool (lowercase, underscores allowed) (required)
* **description**: When the agent should use this tool (required)
* **parameters**: Object defining the tool's input parameters (required)
* **type**: Always `"object"` for the parameters root (required)
* **properties**: Each parameter with its type and description (use `{}` for tools with no inputs)
* **required**: Array of required parameter names (optional)
**For tools without inputs:** You must still include the `parameters` object with `type: "object"` and empty `properties`:
```json theme={null}
{
"type": "function",
"name": "get_current_weather",
"description": "Get the current weather",
"parameters": {
"type": "object",
"properties": {}
}
}
```
### Advanced Webhook Settings
By default, RevRing sends a `POST` request with a JSON body to your webhook URL when a tool is invoked. You can customize this behavior per tool:
| Setting | Description | Default |
| - | - | - |
| **HTTP Method** | The HTTP method used to call your webhook (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`) | `POST` |
| **Headers** | Custom HTTP headers to include in the request (e.g., `Authorization: Bearer token123`) | None |
| **Query Parameters** | Custom query parameters appended to the webhook URL (e.g., `source=revring`) | None |
| **Send Raw Request Body** | When enabled, sends the tool arguments directly as the request body instead of the default `{ callId, tool, arguments }` wrapper | Off |
These settings are configured per-tool in the dashboard or via the API:
```bash theme={null}
curl -X POST https://api.revring.ai/v1/agents/{agentId}/custom-tools \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Check Order",
"webhookUrl": "https://api.yourcompany.com/tools/check-order",
"httpMethod": "POST",
"headers": [
{ "key": "Authorization", "value": "Bearer your-token" }
],
"queryParams": [
{ "key": "source", "value": "revring" }
],
"sendRawRequestBody": false,
"enabled": true,
"schema": {
"type": "function",
"name": "check_order_status",
"description": "Check order status by order ID",
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string", "description": "The order ID" }
},
"required": ["order_id"]
}
}
}'
```
### Webhook Request Format
When the agent invokes your custom tool, RevRing sends a request to your webhook URL.
**Default request body (when Send Raw Request Body is off):**
```json theme={null}
{
"callId": "dc582fd6-4e5c-4860-9ced-abc7390e62a9",
"tool": "check_order_status",
"arguments": {
"order_id": "ORD-12345"
}
}
```
**Raw request body (when Send Raw Request Body is on):**
```json theme={null}
{
"order_id": "ORD-12345"
}
```
Custom tools also run on text channels — [web widget chat](/platform/web-widget) and [SMS](/platform/two-way-messaging). Text invocations use the same request format, with `callId` set to `null` and two extra fields — `conversationId` and `channel` (`"web_chat"` or `"sms"`) — identifying the message thread instead of a call.
**Response Examples:**
You can return either JSON or plain text:
```json theme={null}
{
"status": "out_for_delivery",
"expected_delivery": "2025-11-24T17:00:00Z",
"tracking_number": "1Z999AA10123456784"
}
```
Or:
```
Order ORD-12345 is currently out for delivery. Expected delivery today by 5 PM.
```
The agent will receive the response and incorporate it into the conversation naturally.
### Response Format
Your webhook should return:
* **200 OK** status code
* JSON object or plain text response body
* Response within 8 seconds (recommended: under 3 seconds for best user experience)
**JSON Response Example:**
```json theme={null}
{
"appointments": [
{
"date": "2025-11-25",
"time": "14:00",
"type": "checkup"
}
],
"total_count": 3
}
```
**Plain Text Response Example:**
```
Customer has 3 active appointments. Next appointment is tomorrow at 2 PM.
```
The agent will interpret the response and communicate the information to the caller naturally.
### Best Practices for Custom Tools
#### Clear Descriptions
The agent uses descriptions to decide when to invoke tools. Be specific:
**Good:**
```
Check the status of a customer's order by order ID. Use this when the customer asks about their order, shipment, or delivery status.
```
**Bad:**
```
Check order
```
#### Error Handling
Handle errors gracefully in your webhook. You can return structured error data or error messages:
```json theme={null}
{
"error": "order_not_found",
"message": "Order number not found in system"
}
```
Or plain text:
```
I couldn't find that order number in our system. Could you verify the order number?
```
The agent will communicate the error to the caller appropriately.
#### Performance
* Keep webhook response times under 3 seconds
* Implement timeouts and retries in your webhook code
* Cache frequently accessed data when possible
* Use async processing for complex operations (return immediately with a status message)
#### Security
* Validate requests are coming from RevRing (contact support for HMAC signing setup)
* Sanitize and validate all input parameters
* Use HTTPS for all webhook URLs
* Implement rate limiting to prevent abuse
### Testing Custom Tools
1. Create a simple test tool that returns static data
2. Use the **test panel** to make a call
3. During the call, prompt the agent to use the tool:
* "Can you check order ORD-12345 for me?"
4. Review the transcript to see:
* Tool invocation with arguments
* Your webhook's response
* How the agent incorporated the result into conversation
### Viewing Tool Invocations
All tool invocations appear in call transcripts with detailed information:
* **Tool name**: Which tool was invoked
* **Arguments**: What parameters were passed
* **Status**: Success (ok) or error
* **Response**: What your webhook returned
Navigate to [Call Logs](https://dashboard.revring.ai/app/calls), select a call, and review the transcript pane.
## Tool Limitations
### System Tool Constraints
* **End Call**: Once invoked, the call ends immediately - the agent cannot continue conversation
* **Transfer**: After transfer, the agent disconnects - the call continues with the transfer destination
* **Voicemail Detection**: Only works on outbound calls, not inbound calls
### Custom Tool Constraints
* **Response time**: 8 second timeout - webhooks must respond within this window
* **Concurrent invocations**: Agents invoke tools sequentially, not in parallel
## Managing Tools
### Editing Tools
To modify a custom tool:
1. Navigate to the **Tools** tab
2. Click **Edit** on the tool in the table
3. Update friendly name, webhook URL, schema, or enabled status
4. Click **Save**
Changes take effect immediately for new calls.
### Deleting Tools
To remove a custom tool:
1. Navigate to the **Tools** tab
2. Click **Delete** on the tool in the table
3. Confirm the deletion
Deleting a tool that's actively referenced in your agent's behavior may cause unexpected results. Update your prompt to remove references to deleted tools.
### Disabling Tools Temporarily
Instead of deleting, you can disable tools:
1. Click **Edit** on the tool
2. Toggle **Enabled** to off
3. Click **Save**
The agent will not have access to disabled tools, but the configuration is preserved for future re-enabling.
## Advanced Tool Patterns
### Chaining Tools
Agents can invoke multiple tools in sequence to accomplish complex tasks:
1. Customer asks: "What's my balance and when is my next payment due?"
2. Agent invokes `get_account_balance` → receives balance
3. Agent invokes `get_next_payment_date` → receives date
4. Agent combines both results in response
Design tools to be focused and composable rather than monolithic.
### Conditional Tool Usage
Guide the agent to use tools conditionally based on conversation context:
```
Use the transfer_to_billing tool only when:
- Customer has billing questions you cannot answer
- Customer disputes a charge
- Customer wants to discuss payment plans
Do NOT transfer for simple balance inquiries - use the check_balance tool instead.
```
### Tool Results in Follow-Up
Agents can reference previous tool results later in the conversation:
> Agent: "I checked your order ORD-12345, and it's out for delivery today."
>
> Customer: "Great! And what about my other order?"
>
> Agent: "Let me check that for you..." *\[invokes tool again with new order ID]*
## Troubleshooting
* Verify the tool is enabled (toggle is on)
* Check that the description clearly indicates when to use the tool
* Ensure your prompt doesn't contradict or override the tool's purpose
* Test by explicitly asking the agent to use the tool during a test call
* Review the schema - overly complex parameters may confuse the agent
* Confirm the webhook URL is correct and publicly accessible
* Check that your server is running and responding to POST requests
* Test the endpoint independently with curl or Postman
* Review your server logs for incoming requests
* Verify webhook responds within 8 seconds
* Ensure response is valid JSON or plain text
* Check HTTP status code (should be 200)
* Review your webhook logs for errors or exceptions
* Test the webhook endpoint independently with sample data
* Refine the tool's description to be more specific about usage
* Update your prompt to provide clearer guidance on when to use tools
* Adjust parameter descriptions to clarify expected formats
* Consider splitting complex tools into simpler, focused tools
* Verify the destination number is in valid E.164 format
* Ensure your SIP trunk supports call transfers
* Check that the destination number is reachable
* Review call logs for specific transfer error messages
## Next Steps
Learn about pre-call and post-call webhooks
Manage custom tools programmatically via API
Configure agents for handling inbound calls
Send outbound calls with your agents
# Two-way texting
Source: https://docs.revring.ai/platform/two-way-messaging
Let your agents hold text conversations and send SMS outside of a call
Beyond texting during a call, your agents can hold full back-and-forth text conversations and send messages outside of a call, all from the same number they call from.
There are two parts:
* **Incoming texts:** when someone texts your number, your agent can reply automatically with AI, or RevRing can forward the message to your own system.
* **Sending texts:** send a message from your own automations (for example, following up when a lead does not answer a call) using the [Send Message API](/api-reference/messages/send).
## Replying to incoming texts
Each agent chooses what happens when a text comes in:
RevRing writes a reply using the agent's SMS prompt and the recent conversation, then sends it back. Hands-off for you.
RevRing forwards each incoming text to a URL you choose, so your own system can decide how to respond.
With **Notify your webhook**, RevRing POSTs a `message_received` event to your URL. See [Inbound Message Webhook](/platform/webhooks#inbound-message-webhook) for the exact payload and a code sample.
### The SMS prompt
For automatic replies, the agent uses its **SMS prompt** (set on the agent's **Messaging** tab) when you've added one, and otherwise falls back to the agent's main prompt — so auto-reply works out of the box. Add an SMS prompt only when you want texting to have its own tone, separate from calls. RevRing keeps the recent conversation with each contact, so replies stay in context.
### Knowledge bases, tools, and what texting knows
Text replies use the same [knowledge bases](/platform/knowledge-bases) and [custom tools](/platform/tools) as the agent's calls — nothing separate to set up. Attach a knowledge base or add a tool to the agent, and texting picks it up automatically:
* **Knowledge bases** — replies look up relevant information from the agent's attached knowledge bases, using the agent's existing retrieval settings.
* **Custom tools** — when the conversation calls for it, the agent invokes its custom tools (booking, callbacks, live lookups against your systems) and uses the result in its reply. Tool webhooks receive the same request shape as on a call, with `callId` set to `null` and `conversationId` and `channel` (`"sms"`) identifying the thread instead. Call-only tools (phone transfer, keypad tones) stay call-only.
* **Current date and time** — every reply knows the current date and time in the agent's timezone, so rules like opening hours apply on their own. `{{current_time}}` and the agent's default variables also render in the SMS prompt with `{{variable}}` templating, the same as on calls.
This applies to automatic replies and to API sends with `generate: true` when an `agentId` is provided.
### Set up incoming texts
Follow [Messaging](/platform/messaging) to connect a provider, then open the agent's **Messaging** tab and select the connection.
On the **Messaging** tab, set **When a text comes in** to either reply automatically or notify your webhook. The SMS prompt is optional — auto-reply uses the agent's main prompt if you leave it blank.
On the agent's **Numbers** tab, add the number that will receive texts. Incoming texts are routed to the agent that owns the number.
In your provider, set the inbound message webhook for your Messaging Service to:
```
https://api.revring.ai/webhooks/twilio/inbound
```
To receive texts on a number, assign it to an agent, the same way you do to receive calls. That keeps calls and texts for one number flowing to the same agent.
## Sending texts from your own automations
Use the [Send Message API](/api-reference/messages/send) to send an SMS outside of a call. You can either:
* Send a **raw message** by passing `body`, or
* Have RevRing **compose the message** with AI by passing `generate: true`. It uses the agent's SMS prompt and the ongoing conversation with that contact, or a history you pass inline with `messages` if you prefer to manage memory yourself.
This fits reseller and CRM workflows well. For example, when a lead does not answer a call, your automation platform can trigger a follow-up text.
Messages you send through the API join the same conversation as incoming texts for that contact. So if you send an opener and the contact replies, an automatic reply already has the context of what you sent.
You bring your own provider account, so messages are sent from your numbers and billed to you by your provider, at your provider's rates.
## Managing conversations
RevRing keeps a managed conversation for each contact, on each of your numbers. This is the memory behind automatic replies and AI-composed sends: the recent back-and-forth that keeps responses in context.
* **Automatic idle reset.** After 30 days with no messages, a conversation starts fresh on its own, so a contact who returns much later isn't answered with stale context. This window is configurable per agent.
* **Read the full thread.** Open any conversation on the **Conversations** page to see the whole back-and-forth. When the agent used a tool or its knowledge base to answer, that activity is shown right under the reply, so you can see what the agent did and where its answer came from. The same detail is available on the API with `GET /v1/conversations/{id}`, where each generated reply includes a `meta` object listing the tools it called and the knowledge base sources it cited.
* **Clear a conversation.** See active threads on the **Conversations** page in the dashboard, or with the [Conversations API](/api-reference/conversations/list). Clearing a thread deletes it and its logged messages, so the very next text from that contact starts a brand-new conversation.
Clearing is handy when you want to re-test an agent as if you were a new contact, or to forget a contact's history on request.
## Next steps
Connect a provider and enable texting on a call
Send and generate messages programmatically
The same conversations as a chat bubble on your website
# Versions and Publishing
Source: https://docs.revring.ai/platform/versioning
Every change to an agent is recorded, restorable, and attributable. Choose whether saves go live at once or wait for you to publish.
## Version history
Every write to an agent creates a version automatically. There is no "save a version" step to remember: a prompt edit, a custom tool change, a phone number added or removed, a restore, all of them land in the history with:
* **Who** made the change (the dashboard user's email, or the API key name)
* **What** changed (the list of fields, with before/after values on the version detail)
* **When**
* A full snapshot of the configuration at that point
The history survives agent deletion, so a deleted agent's final configuration is still readable.
See the [List Agent Versions](/api-reference/agents/list-revisions) and [Get Agent Version](/api-reference/agents/get-revision) references. In the dashboard, open the agent and use the **History** tab.
## Restoring a version
Restoring applies a version's configuration back onto the agent through the same validation as a normal save, and records the restore as its own version, so the state you had before stays in the history.
Restore never changes phone numbers or knowledge base links (live routing and knowledge lifecycle are kept as they are), and references to a trunk, messaging connection or folder that no longer exist are skipped and reported.
See [Restore Agent Version](/api-reference/agents/restore-revision).
## Publishing
By default **every save goes live**: the next call uses whatever is saved. That is the right default for a single builder iterating quickly.
When several people work on the same agent, or you want to prepare a change and release it deliberately, switch the agent to **manual publishing**. The agent then has two states:
| State | What it is | Who sees it |
| - | - | - |
| Draft | The configuration as saved in the dashboard or by `PATCH /agents/{id}` | Editors, the dashboard test panel, chat and voice test runs |
| Published | The version conversations run on | Callers and texters: outbound calls, inbound numbers, the web widget (calls and chat), SMS replies |
While you edit, calls keep using the published version. When the draft is ready, **Publish** makes it live for new calls (calls already in progress are not changed).
### Switching modes
```json theme={null}
PATCH /v1/agents/{id}
{ "publishMode": "manual" }
```
Switching to manual publishes the current draft at once, so the agent never silently runs an older state. Switching back to `auto` clears the published version and every save is live again.
The agent carries three fields for this:
* `publishMode`: `auto` or `manual`
* `publishedRevision`: the version calls run on (manual mode); `null` when nothing is published yet, in which case the draft runs
* `hasUnpublishedChanges`: manual mode only, `true` when the draft differs from the published version (on `GET /agents/{id}`; the dashboard shows it as a badge next to the Publish button)
### Publishing
```json theme={null}
POST /v1/agents/{id}/publish
{}
```
Publishes the current draft. The response carries the version calls now run on. Publishing is recorded in the history as a `published` version with the actor, so "who put this live?" is always answerable. Publishing a draft that is already live is a no-op.
To make an earlier version live without touching the draft:
```json theme={null}
POST /v1/agents/{id}/publish
{ "revision": 12 }
```
This is the fastest rollback: the draft keeps your in-progress work, and calls go back to version 12 immediately.
See [Publish Agent](/api-reference/agents/publish).
### Testing before you publish
The dashboard test panel (talk to the agent from the browser) and chat, text and voice test runs use the **draft** by default. That is the point of manual publishing: test the change, then publish it. To check what callers get today, start a run with `"environment": "production"` (or an environment name) on `POST /test-suites/{id}/runs`. Real calls, inbound numbers, the web widget and SMS replies use the published version (prompt, SMS and web-chat prompts, variables, custom tools, voice and conversation settings).
### Which version a call ran on
Every call record and post-call webhook carries `agentRevision`, the version the call ran on (`null` for agents in auto mode). When a caller reports "the agent said X", that number tells you exactly which configuration was live, and the version detail shows the prompt it ran with.
Phone numbers, the SIP trunk, knowledge base links and the agent's active/disabled status are infrastructure, not configuration: they are always live, whatever version is published. Adding a number to an agent in manual mode takes effect at once.
## Environments
Environments let one agent serve more than one audience without duplicating it: for example `staging` pinned to a newer version with its own variable defaults, while `production` stays on the published version.
* `production` always exists. It is the published version and changes through publishing.
* A named environment pins a **version** (or the saved draft, with `revision: null`) and carries **variable defaults** that override the agent's on calls in that environment. Up to 10 per agent.
* Names are lowercase slugs and cannot be renamed.
```json theme={null}
PUT /v1/agents/{id}/environments/staging
{ "revision": 14, "variables": { "tone": "formal", "region": "EU" } }
```
### What runs in an environment
| Entry point | How to choose the environment |
| - | - |
| Outbound call | `environment` on `POST /calls` (omit for production) |
| Inbound number | `environment` on the number (`POST /agents/{id}/phone-numbers` or `PATCH /agents/{id}/phone-numbers/{phoneId}`), or a `trafficSplit` across versions (see [Split calls between versions](#split-calls-between-versions)) |
| Web widget (calls and chat) | `environment` in the agent's widget config |
| SMS replies | Production |
| Chat, text and voice tests | The draft by default; `environment` on the run (`production` or a name) to test a live version |
Unknown names are refused on `POST /calls` with `environment_not_found` before anything is dialed. A number or widget bound to an environment that was later deleted falls back to production rather than failing the caller. Deleting an environment is refused while numbers are still bound to it.
Every call record and post-call webhook carries `environment` (`null` for production) next to `agentRevision`.
See [List Agent Environments](/api-reference/agents/list-environments), [Create or Update Environment](/api-reference/agents/upsert-environment) and [Delete Environment](/api-reference/agents/delete-environment).
### Split calls between versions
Split an inbound number's calls between versions to see which one works better on real callers. In the dashboard, open the agent's **Numbers** tab and choose **Split between versions** for the number; the **History** tab compares the versions. Through the API, give the number a `trafficSplit` of 2 to 5 environments with weights; each call runs the version picked by weight (`null` is production):
```json theme={null}
PATCH /v1/agents/{id}/phone-numbers/{phoneId}
{ "trafficSplit": [ { "environment": null, "weight": 80 }, { "environment": "candidate", "weight": 20 } ] }
```
Every call records the version it ran on (`environment` and `agentRevision`), so you can compare them:
```bash theme={null}
curl "https://api.revring.ai/v1/analytics/versions?agentId=AGENT_ID&from=2026-10-01T00:00:00Z" \
-H "x-api-key: YOUR_API_KEY"
```
Each row is one version with its `calls`, `failureRate`, `avgDurationSeconds`, `negativeSentimentRate`, `evaluationPassRate` and per-criterion pass rates from your [success criteria](/platform/webhooks#post-call-evaluation). The period defaults to the last 7 days (up to 90). When you have a winner, publish it (or point production at it) and remove the split with `"trafficSplit": null`. The number's own `environment` is kept while a split is set, and an environment that a split uses cannot be deleted.
A common setup: keep `production` on manual publishing, point `staging` at the draft (`revision: null`) and bind a spare inbound number to `staging`. Call that number to hear the draft on a real phone; publish when it sounds right.
# Web Widget
Source: https://docs.revring.ai/platform/web-widget
Put your agent on any website as a chat bubble, with memory that follows the visitor
Add your agent to any website as a floating chat bubble. Visitors chat with it right on the page, and the agent remembers each visitor across pages, reloads, and return visits — no account or login needed on your visitor's side.
The widget works on any site, including traditional websites where every click is a full page reload: the conversation picks up exactly where it left off on the next page.
Web chat runs on the same managed conversations as [two-way texting](/platform/two-way-messaging), so everything you know about conversations, memory, and the Messages API applies here too. Web chat messages appear in your Message Logs and Conversations pages with a **Web** badge.
## How it works
1. You enable the widget on an agent and list the websites it may run on.
2. You paste one line of code into the website.
3. Visitors click the bubble and chat. Replies come from your agent's prompt, knowledge bases, and tools, with the recent conversation kept in context per visitor.
4. Every conversation shows up in your dashboard under **Conversations**, and each message in **Message Logs**.
## Setup
Open your agent, go to the **Widget** tab, and list the website domains the widget is allowed to run on (one per line, for example `www.example.com`). Click **Turn on the widget**.
The tab now shows your embed code. Copy it. It looks like this:
```html theme={null}
```
Paste the snippet once, ideally right before the closing `