# Account snapshot
Source: https://docs.revdesk.com/api-reference/account/account-snapshot
/openapi.json get /v1/account
The organization's account snapshot: plan tier and phone-number + caller-trust footprint counts. For the authenticated user and the key's scopes, use GET /v1/me.
# Current identity
Source: https://docs.revdesk.com/api-reference/account/current-identity
/openapi.json get /v1/me
The identity behind the API key: the user, their organization, and the scopes the key carries. For the organization's plan and footprint, use GET /v1/account.
# Configure an AI agent
Source: https://docs.revdesk.com/api-reference/agents/configure-an-ai-agent
/openapi.json patch /v1/agents/{id}
Update an agent's name, greeting, system prompt, voice, language, or enabled state. Changes are synced to the voice runtime so the next call uses them. Send only the fields you want to change.
# Get an AI agent
Source: https://docs.revdesk.com/api-reference/agents/get-an-ai-agent
/openapi.json get /v1/agents/{id}
Fetch a single AI agent by id, including its system prompt.
# List AI agents
Source: https://docs.revdesk.com/api-reference/agents/list-ai-agents
/openapi.json get /v1/agents
Returns every AI agent in the caller's active organization — the agents that answer inbound calls and lead outbound jobs. Use an agent's `id` as `agent_id` on POST /v1/calls or POST /v1/room-token.
# Start a browser call with an AI agent
Source: https://docs.revdesk.com/api-reference/agents/start-a-browser-call-with-an-ai-agent
/openapi.json post /v1/agents/{id}/web-call
Dispatches the agent into a room and returns a browser join token. Hand the token and room_url to @revdesk/webrtc (RevDeskRoom) and talk to the agent in the browser — no phone number needed.
# List appointments
Source: https://docs.revdesk.com/api-reference/appointments/list-appointments
/openapi.json get /v1/appointments
Returns recent appointments (bookings) for the authenticated organization, newest first.
# RevDesk API Authentication & Scopes
Source: https://docs.revdesk.com/api-reference/authentication
Authorize RevDesk with organization-scoped OAuth grants or API keys and least-privilege permissions.
## Bearer token
All API requests require a Bearer token in the `Authorization` header. Registered OAuth clients
use short-lived access tokens; other integrations may use organization-scoped RevDesk API keys.
```bash theme={null}
curl https://api.revdesk.com/v1/calls \
-H "Authorization: Bearer rv_your_api_key_here"
```
## Getting your API key
Create a [RevDesk account](https://www.revdesk.com/signup), then create and manage keys in
**Settings → API Keys**. Key creation is self-serve and does not require a sales conversation. Each
key is prefixed with `rv_` and should be kept secret.
## OAuth authorization code
OAuth-capable clients discover RevDesk's authorization server from the API or MCP protected-resource
metadata. RevDesk supports authorization code with PKCE (`S256`) and refresh tokens. During consent,
a person selects an organization and approves named v1 scopes. Every API request re-checks that the
person is unlocked, the registered client still exists, and the person still belongs to that
organization.
Start with a free trial and a separate development organization. RevDesk reaches real carrier networks
rather than a simulated public sandbox, so use read-only scopes first and restrict calling or messaging
credentials to approved test numbers.
API keys are hashed before storage (SHA-256). The plaintext is shown only once, at creation. If you lose it,
generate a new one.
## Key format
| Component | Example |
| - | - |
| Prefix | `rv_` |
| Token | `a1b2c3d4e5f6...` |
| Full key | `rv_a1b2c3d4e5f6...` |
## Scopes
Every key belongs to one organization and carries an explicit list of **scopes** that gate which
endpoints it can call. Each endpoint declares the scope it requires; a request missing that scope is
rejected with `scope_missing`.
| Scope | Grants |
| - | - |
| `account:read` | Read the account and organization the key belongs to (`GET /v1/account`, `GET /v1/me`). |
| `voice:read` / `voice:write` | Read / manage caller IDs and voice configuration. |
| `voice:webrtc` | Issue browser WebRTC calling tokens (`POST /v1/webrtc-token`). Narrow by design: a key with only this scope can do nothing else. |
| `calls:read` / `calls:write` | Read call metadata / place and control calls. `calls:read` covers metadata only. |
| `recordings:read` | Access call **recording audio** (`recording_url`). Sensitive, and separate from `calls:read`. |
| `transcripts:read` | Access call **transcripts** and analysis (`GET /v1/calls/{id}/transcript`). Sensitive, and separate from `calls:read`. |
| `agents:read` / `agents:write` | List and inspect / configure assistants (`/v1/agents`). See the note below on naming. |
| `phone_numbers:read` / `phone_numbers:write` | List / provision and configure phone numbers. |
| `sms:read` / `sms:write` | Read / send SMS. |
| `webhooks:read` / `webhooks:write` | List / create and delete webhook subscriptions (`/v1/webhook_subscriptions`). |
| `caller_trust:read` / `caller_trust:write` | Read / manage caller-ID trust, enterprise registration, and number reputation. |
| `brand:read` / `brand:write` | Read / submit brand registration for branded calling. |
| `sub_entities:read` / `sub_entities:write` | List / create and update managed sub-entities. |
| `compliance:write` | Change HIPAA mode on a sub-entity. Sensitive, and **never granted by default**. Request it explicitly. |
| `usage:read` | Read usage metrics. |
| `tokens:mint` | Mint short-lived client tokens (`POST /v1/client-tokens`) for browser and mobile callers. |
| `jobs:read` / `jobs:write` | Read / create and manage [Tasks](/concepts/tasks). The scope names are retained for credential compatibility. |
A key created without narrowing gets every scope **except** `compliance:write`, which is opt-in only.
`agents:read` and `agents:write` govern `/v1/agents`, which returns **assistants**: the runtime voices
attached to your numbers and tasks. These are not the same objects as the [agents](/concepts/agents)
you hire in the product, and the counts will not match. There is no `agents` scope.
Keys are **organization-scoped**: every key reads and writes within a single organization, and tenant
isolation is enforced server-side on every endpoint. Grant the **smallest set of scopes** that the
integration needs (e.g. a browser-calling integration only needs `voice:webrtc`).
Lead intake endpoints do not use API keys. Each lead source has a rotatable URL token and source secret so a
CRM or form provider can deliver leads without receiving access to the rest of your workspace. See [Tasks
and lead sources](/getting-started/features/tasks-and-lead-sources#generic-webhook). The retired
`/v1/workflows`, `/v1/workflows/{id}/enroll`, and `/v1/workflow_contacts` endpoints and their workflow-only
scope are no longer available; use `/v1/tasks` for programmatic work. `/v1/jobs` remains a deprecated
compatibility alias.
## Security best practices
* Store API keys in environment variables, never in source code
* Grant the smallest scope set that works (e.g. `voice:webrtc` only for browser calling)
* Rotate keys periodically: revoke the old key in Settings → API Keys, then issue a new one (plaintext is shown once at creation; we only store the SHA-256 hash)
* Use separate keys for development and production
* Monitor key usage via the account endpoint
# Branded Calling
Source: https://docs.revdesk.com/api-reference/branded-calling
Manage your Display Identity Record (DIR) to show your business name, logo, and call reason on recipients' screens.
Branded Calling lets you manage your **Display Identity Record (DIR)** — the business name, logo, and
call reason shown on a recipient's screen when you call. The API is **available in beta**: the
endpoints below are live, and your branded identity appears on more devices as networks approve it.
Branded Calling requires a **vetted enterprise registration** first — your brand is attested to your
legal entity. Complete [Enterprise Registration](/api-reference/enterprise-registration) before
submitting a DIR.
## Endpoints
| Method | Path | Description |
| - | - | - |
| `GET` | `/v1/caller-trust/brand` | Retrieve your current DIR and approval status |
| `PUT` | `/v1/caller-trust/brand` | Create or update your DIR draft (display name, logo, call reason) |
| `POST` | `/v1/caller-trust/brand/submit` | Submit your completed DIR for review |
## DIR fields
| Field | Requirements |
| - | - |
| Display name | Your legal business name, up to 64 characters |
| Call reason | Brief description of why you're calling, up to 120 characters |
| Logo | Square image (PNG, JPEG, or WebP), minimum 200 x 200 px |
## How it works
1. **Draft** your DIR with `PUT /v1/caller-trust/brand` (display name, call reason, logo).
2. **Submit** it for review with `POST /v1/caller-trust/brand/submit`.
3. **Track approval** with `GET /v1/caller-trust/brand` — the status moves from draft to submitted to
approved as review completes. Display on recipients' screens rolls out per network once approved.
To learn how Branded Calling relates to caller-ID reputation and CNAM, see the
[Caller Trust](/concepts/caller-trust) guide.
# Retrieve the caller's Display Identity Record (brand)
Source: https://docs.revdesk.com/api-reference/branded-calling/retrieve-the-callers-display-identity-record-brand
/openapi.json get /v1/caller-trust/brand
Returns the display name, logo, call reasons, and vetting state shown on branded calls.
# Save (or update) the Display Identity Record draft
Source: https://docs.revdesk.com/api-reference/branded-calling/save-or-update-the-display-identity-record-draft
/openapi.json put /v1/caller-trust/brand
Persists the brand (display name, call reasons, authorizer) against the caller's enterprise and mirrors it to the carrier while the DIR is editable. Requires the Branded Calling add-on. Submit for vetting via `POST /v1/caller-trust/brand/submit`.
# Submit the brand profile for vetting
Source: https://docs.revdesk.com/api-reference/branded-calling/submit-the-brand-profile-for-vetting
/openapi.json post /v1/caller-trust/brand/submit
Submits the Display Identity Record to the carrier for vetting. Requires the Branded Calling add-on, a complete brand (display name, call reasons, logo), and a verified authorizer email.
# List caller IDs
Source: https://docs.revdesk.com/api-reference/caller-ids/list-caller-ids
/openapi.json get /v1/caller-ids
Returns all caller IDs for the authenticated team, including verification status and CNAM details.
# Revoke a caller ID
Source: https://docs.revdesk.com/api-reference/caller-ids/revoke-a-caller-id
/openapi.json delete /v1/caller-ids/{id}
Revokes a verified caller ID so it can no longer be used as an outbound caller ID. The record is kept (status REVOKED, active false) so historical calls stay attributed; it still appears in GET /v1/caller-ids with active=false. Re-verify the number to use it again.
# Start caller ID verification
Source: https://docs.revdesk.com/api-reference/caller-ids/start-caller-id-verification
/openapi.json post /v1/caller-ids
Initiates a call-based verification for the given phone number. The carrier calls the number and reads a verification code aloud to whoever answers. The code is held by the carrier and is never returned by this API — proving possession of the number is the point. Submit the code the owner heard via POST /v1/caller-ids/{id}/verify.
# Submit verification code
Source: https://docs.revdesk.com/api-reference/caller-ids/submit-verification-code
/openapi.json post /v1/caller-ids/{id}/verify
Submits the verification code received during the caller ID verification call. On success the caller ID status changes to VERIFIED.
# Update CNAM display name
Source: https://docs.revdesk.com/api-reference/caller-ids/update-cnam-display-name
/openapi.json patch /v1/caller-ids/{id}
Sets the CNAM display name for a verified caller ID. If the caller ID's phone number is also a carrier-provisioned phone, the CNAM update is pushed to the carrier.
# Choosing a calling path
Source: https://docs.revdesk.com/api-reference/calling-paths
The one decision that picks your path: does an AI need to be on a call with a real person, and where should your staff be, in the browser or on a phone?
RevDesk connects people to live calls in a few different ways. They share one security baseline and
differ in **where the audio lives**, **whether the destination is locked by construction**, and
**whether an AI can be on the call**. This page helps you pick.
## Start here: what are you building?
A person talks to an **AI voice agent in the browser**. **No phone number, no carrier, nobody's
phone rings**. The browser opens a WebRTC audio line straight to the AI. This is how the live demo
on our site works, and how an embedded "talk to our AI" widget works.
**This is not one of the three paths below.** It isn't a phone call at all. It's always available.
You're putting a person on a **live phone call to a real number**: outbound dialing, connecting
staff to customers, masked caller ID. This is what the **three paths** (A / B / C) are for. Pick
one with the decision tree below.
**WebRTC is just the transport, not the deciding factor.** WebRTC can carry a call to a carrier
(path A) *or* a line straight to an AI (talk-to-AI, above). So "an AI can only join when RevDesk
places the call", below, is specifically about adding an AI to a call that **also has a person on
a phone**. It does not mean a browser can't reach an AI.
**AI that *answers* your inbound calls** (an agent that picks up when someone dials your
number) is a separate capability, not one of the outbound paths on this page.
## Placing a real call: pick a path
One question does most of the work: **does an AI need to be on the call, or do you need the
destination locked so the client can't change it?** If yes, RevDesk places the call (B or C). If no,
the browser can dial directly (A). Then: is your staff member in the browser, or on their own phone?
```mermaid theme={null}
flowchart TD
Q1{"Need an AI on the call,
or a hard destination lock?"}
Q1 -->|No| A["Path A
Browser dials out"]
Q1 -->|Yes| Q2{"Where is your
staff member?"}
Q2 -->|In the browser| B["Path B
RevDesk places it,
browser joins"]
Q2 -->|On their own phone| C["Path C
RevDesk bridges
both phones"]
```
### At a glance
| | **A — Browser** | **B — Browser, RevDesk-placed** | **C — Bridge (your phone)** |
| - | - | - | - |
| **Status** | Available today | Beta | Available today |
| **Who places the call** | The browser | RevDesk | RevDesk |
| **Your staff talk from** | Browser + mic | Browser + mic | Their own phone |
| **Phones that ring** | One (the destination) | One (the destination) | Two (yours + the destination) |
| **Destination lock** | Scoped & bounded | Hard (server-set) | Hard (server-set) |
| **AI can join the call** | No | Yes (roadmap) | Yes (roadmap) |
| **Needs mic + good network** | Yes | Yes | No, it's a normal phone call |
| **Connect latency** | Instant | Short | Short |
| **In-app controls (mute, transfer)** | Yes | Yes | Limited |
| **Best for** | Simplest internal dialing | In-browser, but with a hard lock or AI | Resilient / regulated / cellular |
## The three paths
Staff click **Call** and talk through their browser; no phone line needed. The browser gets a
scoped, single-use token for that one call and dials the carrier directly.
Quickest to launch and proven-stable. The destination is **declared, attributed, and
guardrail-bounded**, but because the browser is what dials, this path doesn't *lock* the
destination the way B and C do, and there's no room for an AI to join.
**Use it for** simple internal dialing by trusted staff.
Same in-browser experience, but the browser **never dials**. RevDesk places the call and the
browser simply *joins* it, using a room-join token from `POST /v1/room-token` and the RevDesk
browser SDK.
Because the destination is set server-side, it **can't be changed by the client or replayed to
reach a different number**, and because the call lives in a room, you can **add an AI voice**
(mid-call assist, AI voicemail, AI follow-ups; on the roadmap). Only the destination's phone
rings; your staff join from the browser.
**Use it for** an in-browser experience that still needs a hard destination lock or AI on the call.
The path for a regular phone, with no browser or SDK. Staff click **Call**, *their own phone rings*,
and when they answer the other party is already on the line. RevDesk places both legs, so the
destination is locked server-side and the other party only ever sees your RevDesk number, never
the staff member's personal number.
Two phones ring (the staff member's and the destination's). Most resilient, since it falls back to the
cellular network and needs no mic or browser permissions.
**Use it for** regulated / privacy-sensitive calling, or anyone who'd rather answer on a phone.
**For a hard destination lock, use B or C.** Because RevDesk places the call, the destination can't
be changed by the client and the token can't be replayed to reach a different number. Path A scopes
and bounds the destination but doesn't lock it by construction.
## What every path shares (the baseline)
No matter which path you choose:
* **Scoped, single-use tokens.** Each token is issued for exactly one call and invalidated when that call ends, so it can't be reused.
* **You can only call *as* a number you own.** Caller ID is validated at issuance against your verified numbers.
* **Per-call attribution.** Every call is logged to your org, the issuing user, and the from/to. Reconcile via `GET /v1/calls`.
* **Account guardrails.** A destination allowlist, a daily spend cap, a concurrency cap, and a max per-minute rate bound the blast radius even if a token leaked.
* **A call-length limit.** Every call has a maximum duration and is ended automatically if it reaches it, so a stuck or looping call can never run unbounded. Default is **60 minutes**; it can be raised per number or per workspace (contact us or set it in workspace settings), up to a 3-hour ceiling.
## Caller-ID selection and health warnings
`POST /v1/calls/dial` accepts either an exact `from_number` or a `number_set_id`:
```json theme={null}
{
"number_set_id": "YOUR_NUMBER_SET_ID",
"to_number": "+12025550123",
"connect_number": "+12025550124",
"record": false
}
```
If both are supplied, **exact `from_number` wins**. Existing requests using `from_number` continue
to work. Number Set selection is available on `/v1/calls/dial`; it is not an additional input to
the WebRTC or room-token endpoints.
The **202** response includes the resolved `from_number`, `selection_source`
(`exact_from_number` or `number_set_sticky_health`), `evidence_state`, `health_warning`, and
`recommended_action`. A high-risk exact caller ID is dialed with a warning, not silently switched
or rejected for reputation alone. An inactive or technically unroutable exact number still fails
normally, without fallback. Billing, wallet, concurrency, and contractual protections remain.
Number Sets prefer the healthiest eligible members with recipient-sticky selection. Even when
all eligible members are flagged, a call can proceed with a warning; no eligible member is an
error. A preview does not reserve a caller ID: health and membership can change before dialing.
See [Number Health](/api-reference/number-health) for set configuration, evidence states, and
resumable handset testing.
## Ending a call
A call ends when any of these happens (whichever comes first):
* **The AI decides the conversation is over** and hangs up on its own.
* **A person on the call hangs up.**
* **You end it on demand:** `POST /v1/calls/{id}/hangup` stops an in-progress call immediately (ending an already-ended call just returns success).
* **It hits the call-length limit** above.
Testing your agents by calling your own AI numbers (AI-to-AI) is fully supported. Those calls run
like any other and are bounded by the same call-length limit, so they can't get stuck. Use
`POST /v1/calls/{id}/hangup` to end a test call the moment you're done.
Synthetic tests are connectivity QA only: they never count as human readiness evidence or as a
reputation-building campaign. See [Number Health test modes](/api-reference/number-health#1-create-the-session).
## Same surface, swap the path
Paths B and C expose the **same client surface**. Issue a token, attach a `callUpdate` handler, and
switching paths is close to a one-line change:
```ts theme={null}
import { RevDesk } from "@revdesk/sdk";
import { RevDeskRoom } from "@revdesk/webrtc";
const revdesk = new RevDesk({ apiKey: process.env.REVDESK_API_KEY! });
// Path B: browser joins a RevDesk-placed call
const { token, room_url, call_id } = await revdesk.webrtc.getRoomToken({ from_number, to_number });
const call = new RevDeskRoom({ token, roomUrl: room_url, callId: call_id });
call.on("callUpdate", render);
await call.join();
// Path C: bridge to a real phone. RevDesk rings connect_number; the destination
// still only ever sees from_number.
await revdesk.calls.dial({ from_number, to_number, connect_number });
```
See [WebRTC security](/api-reference/webrtc-security) for the token model, caller-ID handling, the
guardrails, and concern-by-concern answers.
# Bridge two phone numbers (from_number ↔ destination)
Source: https://docs.revdesk.com/api-reference/calls/bridge-two-phone-numbers-from_number-↔-destination
/openapi.json post /v1/calls/dial
Bridges two phone numbers into one call — your from_number and the destination. Because it rings two phones, it uses TWO concurrent channels. Pass connect_number to ring a separate phone for the human leg (e.g. an agent's own cell) while the destination still sees from_number; the connect_number is never shown to the destination. Supply from_number or number_set_id; exact from_number always wins when both are present. Number Sets use recipient-sticky selection among the healthiest eligible members. High-risk caller IDs return a health_warning without reputation-based blocking; inactive or technically unroutable exact numbers fail without fallback. Billing, wallet, concurrency, and contractual protections still apply. For 1-channel calls, use POST /v1/calls (AI outbound — the agent joins over the network) or the WebRTC token flow (a human joins in the browser). Returns immediately with call_id, resolved from_number, selection_source, evidence_state, health_warning, and recommended_action; poll GET /v1/calls/{id} or subscribe to webhooks for status. Demo keys must target an allowlisted destination.
# Hang up a call
Source: https://docs.revdesk.com/api-reference/calls/hang-up-a-call
/openapi.json post /v1/calls/{id}/hangup
Ends a call or cancels an unused browser calling ticket. Idempotent for terminal calls. mode=setup_failed records a browser setup error and cancels only unused tickets; mode=cancel_setup cancels only unused tickets. Both setup modes leave originated calls untouched. With no mode, active calls are hung up normally.
# List calls
Source: https://docs.revdesk.com/api-reference/calls/list-calls
/openapi.json get /v1/calls
Returns recent calls for the authenticated organization, newest first. Every call reports has_recording, but the actual recording_url is only populated when the key also holds the recordings:read scope; without it the field is null.
# Place an outbound AI call
Source: https://docs.revdesk.com/api-reference/calls/place-an-outbound-ai-call
/openapi.json post /v1/calls
Dispatches a single-leg outbound AI call to the destination. The agent (assistantId, or your most recently created agent) joins over the network and speaks, so the call uses one channel. For a human dial-bridge call from a specific number, use POST /v1/calls/dial.
# Retrieve a call transcript
Source: https://docs.revdesk.com/api-reference/calls/retrieve-a-call-transcript
/openapi.json get /v1/calls/{id}/transcript
Returns the flat transcript text plus the speaker-diarized `structured_transcript` turns. Requires the `transcripts:read` scope (transcript content is sensitive and gated separately from call metadata). Organizations whose call-content policy pins content to a compliance (HIPAA) boundary receive `403 content_policy_forbidden`. A call with no stored transcript returns `404 not_found`.
# Retrieve a single call
Source: https://docs.revdesk.com/api-reference/calls/retrieve-a-single-call
/openapi.json get /v1/calls/{id}
Returns metadata for the call. `has_recording` reflects whether a recording exists, but the actual `recording_url` is only populated when the key also holds the `recordings:read` scope (recording audio is sensitive and gated separately from call metadata).
# Bounded client tokens
Source: https://docs.revdesk.com/api-reference/client-tokens
Mint short-lived, number-restricted tokens on your server and ship them safely into untrusted client apps.
A **bounded client token** lets your backend hand a calling credential to an untrusted client (a
mobile app, a browser, a partner's device) **without shipping your API key** and without letting that
client call from any number to any number.
You mint a token on your server with your API key. The token is restricted to a set of caller-ID
(`from`) numbers and, optionally, a set of destination (`to`) numbers. The client uses the token in
place of your API key, on the same `Authorization: Bearer` header, and can only place calls within
those bounds. The token is a short-lived, signed `rdc_…` string; it carries no secret you'd mind a
client holding, and it expires on its own.
This is the delegation layer on top of the per-call security already described in the
[WebRTC security model](/api-reference/webrtc-security). Each call placed with a bounded token is
still scoped, attributed, and guardrail-bounded. The token simply guarantees the client can't step
outside the from/to numbers you chose when you minted it.
## How it works
```mermaid theme={null}
sequenceDiagram
participant Backend as Your server (holds API key)
participant API as RevDesk API
participant Client as Your app (untrusted)
Backend->>API: POST /v1/client-tokens { from_numbers, to_numbers, ttl_seconds }
API->>API: verify from_numbers are active numbers you own
API->>API: verify from/to within this key's allowlist ceiling
API-->>Backend: rdc_… token (short-lived, signed)
Backend-->>Client: hand off the rdc_… token
Client->>API: POST /v1/webrtc-token (Authorization: Bearer rdc_…)
API->>API: enforce request from/to ∈ token bounds
API-->>Client: per-call token → place the call
```
## Mint a token (server side)
```ts theme={null}
import { RevDesk } from "@revdesk/sdk";
const revdesk = new RevDesk({ apiKey: process.env.REVDESK_API_KEY! });
const { data } = await revdesk.clientTokens.create({
from_numbers: ["+15551234567"], // caller IDs the client may present (numbers you own)
to_numbers: ["+15557654321"], // optional: destinations the client may dial
ttl_seconds: 900, // optional: 60–3600, default 900
// scopes: ["voice:webrtc"], // optional: subset of this key's scopes
});
// data.token -> "rdc_…" hand this to your client
// data.expires_in, data.from_numbers, data.to_numbers, data.scopes
```
Requires the `tokens:mint` scope on your API key. A minted token **cannot** mint further tokens.
## Use the token (client side)
Construct the SDK (or the `@revdesk/webrtc` browser SDK) with the minted token in place of your API
key. Nothing else changes:
```ts theme={null}
import { RevDesk } from "@revdesk/sdk";
const revdesk = new RevDesk({ apiKey: tokenFromYourServer }); // an rdc_… token
// Allowed: from/to are within the token's bounds
await revdesk.webrtc.getToken({ from_number: "+15551234567", to_number: "+15557654321" });
// Rejected with 403: destination outside the token's to-allowlist
await revdesk.webrtc.getToken({ from_number: "+15551234567", to_number: "+15550009999" });
```
## Binding rules
| Field | Behavior |
| - | - |
| `from_numbers` | Required, at least one. Each must be an **active number your organization owns**. The token may only present these caller IDs. |
| `to_numbers` | **Required, at least one.** The token may only dial these destinations, and they must fall within your key's ceiling. There is no "unrestricted" form: a browser token that could dial anywhere is a toll-fraud liability, so an empty list is rejected. |
| `ttl_seconds` | Optional, `60`–`3600`, default `900`. Revocation is by expiry, so mint short-lived tokens and re-mint as needed. |
| `scopes` | Optional, and the only accepted value is `["voice:webrtc"]` (also the default). A client token can never carry a durable-write scope, mint further tokens, or read recordings or transcripts, no matter what the minting key holds. Anything beyond browser calling has to go through your own server with the real API key. |
## Key-level ceiling
You can also cap an API key itself with **allowed caller IDs** and **allowed destinations** (in the
dashboard under the key's settings, or via the key management API). When set, every token minted from
that key, and every call the key places directly, must fall within that ceiling. Leave the ceiling
blank to allow any number the workspace owns. This lets you, for example, issue one key per customer
that is permanently restricted to that customer's numbers.
## Where bounds are enforced
The from/to bounds are enforced on **every** call-placing endpoint, not just token minting:
`/v1/webrtc-token`, `/v1/room-token`, `/v1/calls`, `/v1/calls/dial`, and `/v1/sms/send`. A request
whose `from` or `to` falls outside the credential's bounds is rejected with `403` before any call is
placed.
# Create or update a contact
Source: https://docs.revdesk.com/api-reference/contacts/create-or-update-a-contact
/openapi.json post /v1/contacts
Upserts a contact by phone number: creates it when no contact with that number exists in the organization, otherwise updates the existing contact's name/email/metadata. The `created` flag in the response tells you which happened (201 on create, 200 on update).
# Delete a contact
Source: https://docs.revdesk.com/api-reference/contacts/delete-a-contact
/openapi.json delete /v1/contacts/{id}
Permanently deletes a contact. Related records (messages, calls) are removed by database cascade. This cannot be undone.
# Get a contact
Source: https://docs.revdesk.com/api-reference/contacts/get-a-contact
/openapi.json get /v1/contacts/{id}
Returns a single contact by id.
# List contacts
Source: https://docs.revdesk.com/api-reference/contacts/list-contacts
/openapi.json get /v1/contacts
Returns recent contacts for the authenticated organization, newest first.
# Update a contact
Source: https://docs.revdesk.com/api-reference/contacts/update-a-contact
/openapi.json patch /v1/contacts/{id}
Updates a contact's name, email, company, or metadata. Fields omitted from the body are left unchanged; pass null to clear a nullable field. The phone number is immutable — create a new contact instead.
# Conventions
Source: https://docs.revdesk.com/api-reference/conventions
Errors, idempotency, and pagination: the HTTP-level conventions that apply to every endpoint.
The RevDesk v1 API follows a consistent shape across every endpoint. The conventions on this page apply everywhere, so you can write your client code once and reuse the patterns.
## Errors
Every error response uses the same envelope:
```json theme={null}
{
"error": {
"code": "validation_error",
"message": "caller_id must be E.164 format",
"fields": {
"caller_id": "Expected +"
}
}
}
```
When a request fails validation, the `fields` map contains a per-field explanation so you
can surface the right error next to the right input. Call-failure errors additionally carry
a `category`, a generalized bucket you can show an end user.
See **[Errors](/api-reference/errors)** for the full list of codes and categories, including
every reason a call can fail to be placed or connect.
***
## Idempotency
Every `POST`, `PUT`, `PATCH`, and `DELETE` endpoint supports the `Idempotency-Key` header. Set it to a unique value (a UUID is recommended) on your first request, and retry the same request with the same header to safely replay.
```bash theme={null}
curl -X POST https://api.revdesk.com/v1/calls \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: 9b8cfa7a-1c2d-4a88-b3c0-1d2e3f4a5b6c" \
-H "Content-Type: application/json" \
-d '{"caller_id": "+14155551234", "destination": "+14155556789"}'
```
### Semantics
* **First request with a key.** We run the handler and persist the response under the key.
* **Repeat with the same key and same body.** We replay the saved response verbatim, including status code. The replay carries an `Idempotency-Replay: true` response header so you can tell.
* **Repeat with the same key but a different body.** We return `409 idempotency_conflict` with the original key's request id in `fields.conflicts_with`.
### TTL
Idempotency records live for **24 hours** after the first successful response. After the window, reusing the key is treated as a fresh request.
### Best practices
* Use a new key for each new logical operation. Reusing keys across different calls drops traffic.
* The key is scoped to the API key it was submitted under. Rotating keys resets the idempotency window.
* Idempotency only protects the RevDesk side. Don't rely on it for cross-process transactions (for example, a call placed plus a Stripe charge).
### When to set one
* Retrying a request after a network timeout: critical.
* Background jobs that may re-run after a worker crash: critical.
* Interactive UIs with a double-click-vulnerable button: nice to have.
### With the SDK
[`@revdesk/sdk`](/api-reference/sdks) takes an `idempotencyKey` on any mutation and sends it as the
`Idempotency-Key` header. Use `generateIdempotencyKey()` or supply your own:
```ts theme={null}
import { generateIdempotencyKey } from "@revdesk/sdk";
await revdesk.sms.send(
{ from_number, to_number, body: "Hi!" },
{ idempotencyKey: generateIdempotencyKey() },
);
```
***
## Pagination
Every list endpoint returns the same envelope:
```json theme={null}
{
"data": [ /* items */ ],
"meta": {
"cursor": "opaque-string",
"total_results": 42,
"page_size": 20
}
}
```
Pass `?cursor=` on the next request to walk to the next page. `meta.cursor` is omitted on the last page. Cursors are opaque to clients: treat the value as a bag of bytes, do not parse it.
### Example
```bash theme={null}
# First page
curl "https://api.revdesk.com/v1/calls?limit=50" \
-H "Authorization: Bearer $KEY"
# Next page
curl "https://api.revdesk.com/v1/calls?limit=50&cursor=eyJpZCI6MTIzfQ" \
-H "Authorization: Bearer $KEY"
```
### Limits
* `limit` default is **20**, max **100**.
* Cursors are valid for **24 hours** from issuance. Requesting an expired cursor returns `400 validation_error` with `fields.cursor`.
* Ordering is **by creation time, newest first** for every list endpoint. Use `?order=asc` where supported to reverse.
### With the SDK
[`@revdesk/sdk`](/api-reference/sdks) handles cursors for you. `list()` returns one page; `listAll()`
is an async iterator that walks every page:
```ts theme={null}
for await (const number of revdesk.phoneNumbers.listAll()) {
console.log(number.phone_number);
}
```
`listAll()` is available on `phoneNumbers`, `subEntities`, and `callerTrust.reputation.numbers`.
# Get document status
Source: https://docs.revdesk.com/api-reference/documents/get-document-status
/openapi.json get /v1/caller-trust/documents/{id}
Returns metadata for a previously uploaded document, including its verification status and antivirus scan status.
# Upload a document (e.g. signed LOA)
Source: https://docs.revdesk.com/api-reference/documents/upload-a-document-eg-signed-loa
/openapi.json post /v1/caller-trust/documents
Uploads a base64-encoded PDF to the carrier's document API. Returns a document ID that can be passed to the reputation enable endpoint. Documents expire after 30 minutes if not linked to a service — upload immediately before enabling reputation.
# Registration Requirements
Source: https://docs.revdesk.com/api-reference/enterprise-registration
What enterprise registration unlocks, and which caller-identity features need an EIN, and which don't.
## When registration is required
Not every caller-identity feature requires registering a business. The dividing line is whether a
feature puts **your own legal brand** in front of recipients or carriers. That's what needs a
verified enterprise (a Federal **EIN** + a signed **Letter of Authorization**). Lighter-weight
features work without one.
| Capability | Needs EIN + LOA? | Notes |
| - | - | - |
| **CNAM** display name | **No** | Set the caller-ID name shown on the network. A carrier-database entry, not a brand vetting. |
| **Number reputation monitoring** | **No** | Every number you provision is auto-enrolled under our shared umbrella, with live spam-risk scores from day one and no paperwork. |
| **Branded Calling** (your name, logo, call reason on the screen) | **Yes** | The rich on-screen identity is attested to your legal entity, so the carrier requires a vetted enterprise. |
| **Own-brand reputation & remediation** (dispute/fix spam labels under your entity) | **Yes** | Actively managing reputation as your own brand (rather than riding the umbrella) requires your registered enterprise. |
So: **CNAM and baseline reputation monitoring need no EIN.** **Branded Calling and own-brand
reputation/remediation do.** You can ship calls and watch reputation on day one, then register an
enterprise when you want your own brand on the screen.
## Registering an enterprise
When you do need it (Branded Calling or own-brand reputation), the flow is:
`POST /v1/caller-trust/enterprise` with your legal business details. Starts in `DRAFT`.
`POST /v1/caller-trust/enterprise/generate-loa` produces the authorization PDF; sign and submit
with `POST /v1/caller-trust/enterprise/sign-and-submit` (or `…/submit` if signed out of band).
Status moves `PENDING_VETTING → APPROVED` (or `REJECTED` → fix and resubmit). Poll
`GET /v1/caller-trust/enterprise` or subscribe to the `enterprise.status.updated` webhook.
Once `APPROVED`, your Display Identity Record becomes eligible. Submit it via
[Branded Calling](/concepts/caller-trust) to push your name, logo, and reason to recipients.
## Working with a specific registration
`/v1/caller-trust/enterprise` always refers to your **primary** registration — for almost every
account that's the only one, and the flow above is all you need. If your account holds more than
one registration (for example a draft created while an earlier one is still vetting), every
operation is also addressable by registration ID:
| Operation | Endpoint |
| - | - |
| Fetch one registration | `GET /v1/caller-trust/enterprise/{id}` |
| Edit a `DRAFT` | `PATCH /v1/caller-trust/enterprise/{id}` |
| Delete a registration | `DELETE /v1/caller-trust/enterprise/{id}` |
| Submit to the carrier | `POST /v1/caller-trust/enterprise/{id}/submit` |
| Sign LOA + submit | `POST /v1/caller-trust/enterprise/{id}/sign-and-submit` |
| Generate LOA for hand-signing | `POST /v1/caller-trust/enterprise/{id}/generate-loa` |
| Re-sign a rejected LOA | `POST /v1/caller-trust/enterprise/{id}/resign` |
The `{id}` is the `id` returned when you create the draft (`POST /v1/caller-trust/enterprise`).
Both spellings hit the same underlying operations — the id-addressed form just pins which
registration they act on.
For the full two-tier model (shared umbrella vs. own-brand) and how reputation and branded calling
relate, see the [Caller Trust](/concepts/caller-trust) concept guide.
# Create or update the caller's enterprise registration draft
Source: https://docs.revdesk.com/api-reference/enterprise-registration/create-or-update-the-callers-enterprise-registration-draft
/openapi.json post /v1/caller-trust/enterprise
Idempotent upsert of the own-brand enterprise registration as DRAFT. No carrier-side calls are made here — submission to the carrier happens in a separate sign-and-submit step.
# Delete an enterprise registration by ID
Source: https://docs.revdesk.com/api-reference/enterprise-registration/delete-an-enterprise-registration-by-id
/openapi.json delete /v1/caller-trust/enterprise/{id}
Deletes a specific enterprise registration. DRAFT records that were never submitted to the carrier are removed directly. Active records are torn down with the carrier and marked DELETED locally.
# Delete the caller's enterprise registration
Source: https://docs.revdesk.com/api-reference/enterprise-registration/delete-the-callers-enterprise-registration
/openapi.json delete /v1/caller-trust/enterprise
Deletes the own-brand enterprise registration. DRAFT records that were never submitted to the carrier are removed directly. Active records are torn down with the carrier (enterprise + reputation) and marked DELETED locally.
# Generate LOA PDF for hand-signing
Source: https://docs.revdesk.com/api-reference/enterprise-registration/generate-loa-pdf-for-hand-signing
/openapi.json post /v1/caller-trust/enterprise/generate-loa
Generates a Letter of Authorization PDF pre-filled with enterprise data and a blank hand-signature line. Enterprise must have been submitted to the carrier first (has carrier_enterprise_id).
# Generate LOA PDF for hand-signing (by ID)
Source: https://docs.revdesk.com/api-reference/enterprise-registration/generate-loa-pdf-for-hand-signing-by-id
/openapi.json post /v1/caller-trust/enterprise/{id}/generate-loa
Generates a Letter of Authorization PDF pre-filled with enterprise data and a blank hand-signature line. Enterprise must have been submitted to the carrier first (has carrier_enterprise_id). Addresses a specific registration by ID.
# Get an enterprise registration by ID
Source: https://docs.revdesk.com/api-reference/enterprise-registration/get-an-enterprise-registration-by-id
/openapi.json get /v1/caller-trust/enterprise/{id}
Returns a specific enterprise registration by its ID.
# Get the caller's enterprise registration
Source: https://docs.revdesk.com/api-reference/enterprise-registration/get-the-callers-enterprise-registration
/openapi.json get /v1/caller-trust/enterprise
Returns the own-brand enterprise record if one has been registered, or null otherwise.
# Re-sign the LOA for an enterprise
Source: https://docs.revdesk.com/api-reference/enterprise-registration/re-sign-the-loa-for-an-enterprise
/openapi.json post /v1/caller-trust/enterprise/resign
Use when the on-file LOA needs to be replaced. Replaces the LOA with the carrier; the old LoaSignature row is preserved for audit. Status resets to PENDING_VETTING.
# Re-sign the LOA for an enterprise (by ID)
Source: https://docs.revdesk.com/api-reference/enterprise-registration/re-sign-the-loa-for-an-enterprise-by-id
/openapi.json post /v1/caller-trust/enterprise/{id}/resign
Use when the on-file LOA needs to be replaced. Replaces the LOA with the carrier; the old LoaSignature row is preserved for audit. Status resets to PENDING_VETTING. Addresses a specific registration by ID.
# Sign LOA + submit enterprise to carrier
Source: https://docs.revdesk.com/api-reference/enterprise-registration/sign-loa-+-submit-enterprise-to-carrier
/openapi.json post /v1/caller-trust/enterprise/sign-and-submit
DRAFT -> PENDING_VETTING in one call. Generates + signs the LOA PDF, uploads it to the carrier, creates the carrier enterprise record, and enables Number Reputation for it.
# Sign LOA + submit enterprise to carrier (by ID)
Source: https://docs.revdesk.com/api-reference/enterprise-registration/sign-loa-+-submit-enterprise-to-carrier-by-id
/openapi.json post /v1/caller-trust/enterprise/{id}/sign-and-submit
DRAFT -> PENDING_VETTING in one call. Generates + signs the LOA PDF, uploads it to the carrier, creates the carrier enterprise record, and enables Number Reputation for it. Addresses a specific registration by ID.
# Submit enterprise to the carrier
Source: https://docs.revdesk.com/api-reference/enterprise-registration/submit-enterprise-to-the-carrier
/openapi.json post /v1/caller-trust/enterprise/submit
Accepts Number Reputation Terms of Service and creates the enterprise with the carrier. Does NOT sign an LOA or enable reputation — those are separate steps.
# Submit enterprise to the carrier (by ID)
Source: https://docs.revdesk.com/api-reference/enterprise-registration/submit-enterprise-to-the-carrier-by-id
/openapi.json post /v1/caller-trust/enterprise/{id}/submit
Accepts Number Reputation Terms of Service and creates the enterprise with the carrier. Does NOT sign an LOA or enable reputation — those are separate steps. Addresses a specific registration by ID.
# Update a DRAFT enterprise registration
Source: https://docs.revdesk.com/api-reference/enterprise-registration/update-a-draft-enterprise-registration
/openapi.json patch /v1/caller-trust/enterprise/{id}
Updates fields on a DRAFT enterprise. Rejects updates to enterprises that have left DRAFT status.
# Errors
Source: https://docs.revdesk.com/api-reference/errors
Every error the API can return: a granular code for your integration logic, and a generalized category you can show an end user.
When a request fails, RevDesk returns a consistent error envelope. Call-related errors
carry **two tiers** so you can act on them precisely *and* show something friendly to a
person:
* **`code`**: a granular, machine-readable reason (e.g. `call_id_not_verified`,
`busy`, `agent_dispatch_failed`). Branch on this in your integration.
* **`category`**: a generalized, user-facing bucket (e.g. `invalid_request`,
`provider_error`). Map it straight to a message you display to an end user.
```json theme={null}
{
"error": {
"code": "call_id_not_verified",
"category": "invalid_request",
"message": "The caller ID isn't verified for this account.",
"resolution_hint": "Verify the caller ID or use a number already owned by this workspace.",
"doc_url": "https://docs.revdesk.com/api-reference/errors#invalid-request"
}
}
```
`message` is human-readable but may change; never branch on it. `resolution_hint` gives a
short recovery step that an agent or developer can act on. `fields` (when present) maps a
request field to a per-field explanation. `doc_url` deep-links to the relevant section below.
## Categories
The generalized bucket. If you only surface one thing to an end user, surface this.
| Category | What it means | What the caller can do |
| - | - | - |
| `invalid_request` | The request was malformed: bad/missing destination, caller ID not verified, no number on the account | Fix the request |
| `compliance_block` | Blocked by a calling rule: DNC, opt-out, no consent, calling window/day, frequency cap, emergency number | Remove from list / wait for the window |
| `account_limit` | An account-level limit: minutes exhausted, no active subscription, payment required, rate/concurrency cap | Upgrade / add payment / retry later |
| `unreachable` | Placed, but never reached a person: busy, no answer, invalid/disconnected number, carrier rejected | Retry later |
| `voicemail` | Reached an answering machine | Informational |
| `provider_error` | A RevDesk / telephony infrastructure failure | Retry, then contact support |
## When you receive them
`invalid_request`, `compliance_block`, `account_limit`, and `provider_error` codes come
back **synchronously** on the dial request (with the HTTP status shown below).
`unreachable` and `voicemail` describe how a *placed* call ended; they are **not** returned
on the dial request. They arrive on the call resource (`failure_code` / `failure_category`)
and on the `phone_call.ended` webhook once the call completes.
## Invalid request
`400` / `409` / `422`: returned synchronously.
| Code | HTTP | Retryable | Meaning |
| - | - | - | - |
| `destination_missing` | 400 | no | No destination phone number was provided |
| `destination_invalid` | 400 | no | The destination isn't a valid, dialable E.164 number |
| `no_active_number` | 409 | no | No active phone number is available to place the call (e.g. still provisioning) |
| `call_id_not_verified` | 422 | no | The caller ID isn't verified for this account |
## Compliance block
`403` / `429`: returned synchronously. The call was refused by a calling rule.
| Code | HTTP | Retryable | Meaning |
| - | - | - | - |
| `destination_blocked` | 403 | no | Emergency or special-service number that can't be dialed |
| `dnc_suppressed` | 403 | no | The number is on a Do-Not-Call list |
| `recipient_opted_out` | 403 | no | The contact has opted out of calls |
| `contact_automation_paused` | 403 | no | Automated outreach is paused for this contact after a clear decline |
| `no_consent` | 403 | no | No calling consent is on file for the contact |
| `outside_call_window` | 403 | yes | It's outside the permitted calling hours for the recipient |
| `day_not_allowed` | 403 | yes | Calls aren't permitted to the recipient today |
| `frequency_exceeded` | 429 | yes | The contact has already been called the maximum number of times |
## Texting not ready
`403`: returned synchronously by `POST /v1/sms/send` when the workspace's A2P/10DLC texting
registration blocks the send. Not retryable until the registration state changes —
subscribe to the `TEXTING_REGISTRATION_STATUS_CHANGED` webhook or check `texting_status`
on `GET /v1/phone-numbers` before sending.
| Code | HTTP | Retryable | Meaning |
| - | - | - | - |
| `texting_not_ready` | 403 | no | The A2P texting registration is missing, pending carrier review, or rejected. `fields.reason` carries the specific gate reason (`a2p_registration_required` / `a2p_registration_pending` / `a2p_registration_rejected` / `no_sender`) and `fields.registration_status` the coarse `none` / `pending` / `rejected` state |
## Content policy forbidden
`403`: returned synchronously by `GET /v1/calls/{id}/transcript` (and other call-content endpoints)
when the organization's call-content policy pins content to a compliance (HIPAA) boundary. Transcripts,
recordings, and derived analysis are never returned over the public API for these organizations.
| Code | HTTP | Retryable | Meaning |
| - | - | - | - |
| `content_policy_forbidden` | 403 | no | The organization's policy forbids returning call content (transcripts/recordings) over the API |
## Account limit
`402` / `429`: returned synchronously.
| Code | HTTP | Retryable | Meaning |
| - | - | - | - |
| `voice_usage_exhausted` | 402 | no | Your included calling minutes are used up |
| `no_active_subscription` | 402 | no | No active subscription is available to place calls |
| `no_number_available` | 429 | yes | All of your numbers are busy right now |
| `rate_limited` | 429 | yes | Too many calls are being placed at once |
## Unreachable
Asynchronous: these appear on the call resource and the `phone_call.ended` webhook, not on
the dial response.
| Code | Retryable | Meaning |
| - | - | - |
| `busy` | yes | The line was busy |
| `no_answer` | yes | No one answered |
| `number_unreachable` | no | The number could not be reached (invalid or disconnected) |
| `call_declined` | no | The recipient declined the call |
| `carrier_rejected` | no | The carrier rejected the call |
## Voicemail
Asynchronous: appears on the call resource and the `phone_call.ended` webhook.
| Code | Retryable | Meaning |
| - | - | - |
| `voicemail_reached` | yes | Reached an answering machine |
## Provider error
`500` / `502`: returned synchronously. A transient RevDesk / telephony infrastructure
failure; safe to retry.
| Code | HTTP | Retryable | Meaning |
| - | - | - | - |
| `room_creation_failed` | 502 | yes | We couldn't start the call session |
| `agent_dispatch_failed` | 502 | yes | We couldn't connect an agent to the call |
| `sip_gateway_failed` | 502 | yes | We couldn't reach the phone network |
| `provider_api_error` | 502 | yes | The telephony provider returned an error |
| `internal_error` | 500 | yes | An unexpected server error occurred |
## Example
Place a call:
```bash theme={null}
curl -X POST https://api.revdesk.com/v1/calls/dial \
-H "Authorization: Bearer $REVDESK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"from_number": "+14155551234", "to_number": "+14155556789"}'
```
A successful dial returns `202 Accepted` with the `call_id`; subscribe to the webhook for
the final outcome. Representative failures:
```jsonc theme={null}
// 422: the from_number isn't verified on this account
{
"error": {
"code": "call_id_not_verified",
"category": "invalid_request",
"message": "The caller ID isn't verified for this account.",
"doc_url": "https://docs.revdesk.com/api-reference/errors#invalid-request"
}
}
// 409: the number exists but isn't active yet
{
"error": {
"code": "no_active_number",
"category": "invalid_request",
"message": "from_number is not yet active; wait for provisioning to finish.",
"doc_url": "https://docs.revdesk.com/api-reference/errors#invalid-request"
}
}
// 502: couldn't connect an agent to the call (safe to retry)
{
"error": {
"code": "agent_dispatch_failed",
"category": "provider_error",
"message": "We couldn't connect an agent to the call. Please try again.",
"doc_url": "https://docs.revdesk.com/api-reference/errors#provider-error"
}
}
```
An `unreachable` / `voicemail` outcome shows up later on the call resource:
```jsonc theme={null}
{
"data": {
"id": "call_a1b2c3",
"status": "busy",
"call_outcome": "busy",
"failure_code": "busy",
"failure_category": "unreachable",
"disconnection_reason": "user_busy"
}
}
```
## HTTP and authentication errors
These apply to every endpoint, not just calls. They use the same envelope (without a
`category`).
| Code | HTTP | Description |
| - | - | - |
| `validation_error` | 400 | Request body or parameters are invalid (see `fields`) |
| `unauthorized` | 401 | Missing or invalid API key |
| `forbidden` | 403 | API key does not have access to this resource |
| `not_found` | 404 | The requested resource does not exist |
| `idempotency_conflict` | 409 | An `Idempotency-Key` already succeeded with a different body |
| `rate_limited` | 429 | Too many requests, slow down |
| `internal_error` | 500 | An unexpected server error occurred |
## With the SDK
[`@revdesk/sdk`](/api-reference/sdks) throws a typed `RevDeskError` for every non-2xx response,
mapping the envelope above onto these fields:
| Property | From the envelope | Notes |
| - | - | - |
| `code` | `error.code` | The granular, machine-readable reason. Branch on this. |
| `category` | `error.category` | The user-facing bucket (call-failure errors only). |
| `message` | `error.message` | Human-readable; may change. Don't branch on it. |
| `resolutionHint` | `error.resolution_hint` | Short, actionable recovery guidance, when present. |
| `fields` | `error.fields` | Per-field validation messages, when present. |
| `docUrl` | `error.doc_url` | Deep link to the relevant docs section. |
| `status` | HTTP status | The numeric status code. |
| `requestId` | `x-request-id` header | Echo to support when reporting an issue. |
`error.isRetryable` is `true` for `429` and `5xx` responses.
```ts theme={null}
import { RevDeskError } from "@revdesk/sdk";
try {
await revdesk.calls.dial({ from_number, to_number });
} catch (err) {
if (err instanceof RevDeskError && err.code === "call_id_not_verified") {
// prompt the user to verify their caller ID
}
}
```
See [Authentication](/api-reference/authentication) for `scope_missing` and demo-key errors,
and [Conventions](/api-reference/conventions) for idempotency and pagination.
# RevDesk API Documentation
Source: https://docs.revdesk.com/api-reference/index
The RevDesk API places calls, sends messages, manages contacts and numbers, and runs tasks.
## Overview
The RevDesk API places calls, sends messages, manages contacts and numbers, and runs tasks, with custom caller ID, CNAM branding, call recording, and compliance controls.
**Base URL:** `https://api.revdesk.com/v1`
Building in TypeScript? Skip the raw HTTP and use the official [`@revdesk/sdk`](/api-reference/sdks) (typed
REST client) and [`@revdesk/webrtc`](/api-reference/sdks) (browser calling). See [Official
SDKs](/api-reference/sdks).
## What you can do
Initiate branded outbound calls with caller ID masking and optional recording.
Verify phone numbers and register CNAM display names for your business.
Monitor call minutes, costs, and usage against your plan.
Access dual-channel call recordings for compliance and quality review.
## Quick start
[Create a RevDesk account](https://www.revdesk.com/signup), then create an organization-scoped key in
**Settings → API Keys**. This is self-serve and does not require contacting sales. Choose only the scopes
your integration needs; the plaintext key is shown once.
Register and verify the phone number you want to display when making calls.
Bridge a call with [`POST /v1/calls/dial`](/api-reference/quickstart), or see [Choosing a calling
path](/api-reference/calling-paths) to pick the right approach.
Calls return immediately as `QUEUED`. Poll [`GET /v1/calls/{id}`](/api-reference/quickstart) for status,
duration, and recording, or subscribe to webhooks.
New here? Start with the [Quickstart](/api-reference/quickstart) for an end-to-end first call.
## Rate limits
All endpoints are rate limited to **600 requests per minute** per API key (120/min for demo keys).
Rate-limit headers (`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`) are returned
on each response. The request rate is separate from how many calls can run at once. Concurrent
capacity is set by your account's provisioned call capacity. Need more? Contact us to raise it.
# Placing & Load-Testing Calls
Source: https://docs.revdesk.com/api-reference/load-testing
Place outbound calls via the API and verify concurrent throughput across your numbers before you go live.
The Voice API places calls **synchronously** with **no per-account concurrency cap**, so you can fan
calls out concurrently across every number you've provisioned. This guide shows how to place a call
and run a safe concurrency test so you can confirm capacity for your own volume.
## Before you start
Issued from your dashboard. (Demo keys are capped at 120 req/min and restricted to an allowlist of
destinations; production keys allow 600 req/min and any destination.)
Calls originate from a number on your account.
For a load test, dial numbers you control, never real third parties.
All requests go to `https://api.revdesk.com`. Rate-limit headers (`X-RateLimit-Limit`,
`X-RateLimit-Remaining`, `X-RateLimit-Reset`) are returned on every response.
## Place a call
```bash theme={null}
curl -X POST https://api.revdesk.com/v1/calls/dial \
-H "Authorization: Bearer $REVDESK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from_number": "+1XXXXXXXXXX",
"to_number": "+1YYYYYYYYYY",
"record": true
}'
# → 202 { "data": { "call_id": "…", "status": "QUEUED", … } }
```
The call is accepted immediately (`202`) and progresses asynchronously. Check its status anytime:
```bash theme={null}
curl https://api.revdesk.com/v1/calls/ \
-H "Authorization: Bearer $REVDESK_API_KEY"
```
## Run a concurrency test
The pattern: fire N calls at once, hold a concurrency cap, and record success rate + latency as you
ramp. Below is a self-contained Node script with no dependencies beyond `fetch`.
```js theme={null}
// load-test.mjs, usage: REVDESK_API_KEY=... node load-test.mjs
const KEY = process.env.REVDESK_API_KEY;
const BASE = "https://api.revdesk.com";
const FROM = ["+1XXXXXXXXXX"]; // numbers you own (rotate across your pool)
const TO = ["+1YYYYYYYYYY"]; // destinations you control
const RAMP = [3, 6, 10, 20]; // concurrent calls per wave
const COOLDOWN_MS = 15_000;
async function placeOne(i) {
const t0 = performance.now();
const res = await fetch(`${BASE}/v1/calls/dial`, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${KEY}` },
body: JSON.stringify({
from_number: FROM[i % FROM.length],
to_number: TO[i % TO.length],
record: false,
}),
});
const ms = Math.round(performance.now() - t0);
const body = await res.json().catch(() => ({}));
return { ok: res.ok, status: res.status, ms, callId: body?.data?.call_id };
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
for (const n of RAMP) {
const results = await Promise.all(Array.from({ length: n }, (_, i) => placeOne(i)));
const ok = results.filter((r) => r.ok).length;
const lat = results.map((r) => r.ms).sort((a, b) => a - b);
const p95 = lat[Math.min(lat.length - 1, Math.floor(0.95 * lat.length))];
console.log(`n=${n} ok=${ok}/${n} p95=${p95}ms 429s=${results.filter((r) => r.status === 429).length}`);
if (ok / n < 0.9) { console.log(`fail rate too high at n=${n}, stopping`); break; }
if (n !== RAMP.at(-1)) await sleep(COOLDOWN_MS);
}
```
Each call places a real call and consumes minutes/credits. Keep `record` off and waves modest while
testing, and dial only numbers you control.
## Concurrency & capacity
Capacity is measured in **concurrent channels**: one channel carries one in-progress call *leg*.
Your account has a provisioned concurrent-channel limit, sized to your expected peak. When you exceed
it, additional calls are **rejected**: the placement fails, calls don't queue. It's a provisioned
setting, not a hard platform limit; tell us your peak and we raise it ahead of launch.
How many concurrent *calls* that allows depends on the path, since a channel is per leg:
| Path | Channels per call | Concurrent calls at a 100-channel limit |
| - | - | - |
| AI outbound (`POST /v1/calls`), agent joins over the network | **1** | **100** |
| Human via browser/app (WebRTC token + 1 leg) | **1** | **100** |
| Phone-to-phone bridge (`POST /v1/calls/dial`), rings both numbers | **2** | **50** |
For maximum concurrency, use a path where only the destination is on the phone network (the agent or
rep joins over WebRTC). That's 1 channel per call. The dial bridge rings two phones, so it uses 2.
Two independent limits to keep distinct:
* **Request rate.** 600 requests/minute per key. Exceeding it returns `429` with `X-RateLimit-*` headers.
* **Concurrent channels.** Your provisioned limit. Exceeding it **fails the call placement** with a
channel-limit error; it is *not* a `429`.
## What to measure
| Metric | Where | Healthy |
| - | - | - |
| Accepted | response code | `202` accepted (`429` = request-rate limited) |
| Call status | `GET /v1/calls/{id}` | reaches `ANSWERED` / `COMPLETED`, not `FAILED` |
| Channel ceiling | first wave that starts failing placement | equals your provisioned channel limit ÷ channels-per-call |
## Results
A call is accepted (`202`) and dispatched asynchronously; subscribe to webhooks or poll
`GET /v1/calls/{id}` for live status. The ceiling is your provisioned concurrent-channel limit, at
which further placements return a channel-limit error. We size that limit to your peak before go-live.
Fill the table for your account and path:
| Concurrent calls | Placed | Succeeded | Failed | p95 accept | Notes |
| - | - | - | - | - | - |
| 25 | | | | | |
| 50 | | | | | |
| 100 | | | | | |
Planning for high volume? Tell us your **peak expected concurrency** and we'll provision capacity
ahead of your launch.
# RevDesk MCP Server
Source: https://docs.revdesk.com/api-reference/mcp
Connect AI agents to RevDesk over the Model Context Protocol.
RevDesk's remote [Model Context Protocol](https://modelcontextprotocol.io) server gives AI agents a
small, auditable tool surface for calling, SMS, phone numbers, caller IDs, and usage. It uses
Streamable HTTP and the same organization-scoped OAuth grants, API keys, and least-privilege scopes
as the REST API.
**Server URL:** `https://mcp.revdesk.com/mcp`
**Discovery:** `https://mcp.revdesk.com/.well-known/mcp`
**Official registry:**
[`io.github.cell-labs-inc/revdesk-product`](https://registry.modelcontextprotocol.io/v0/servers/io.github.cell-labs-inc%2Frevdesk-product/versions/1.1.0)
Agents may initialize the server and inspect tool names and schemas without a credential. Reading
organization data or invoking any tool requires a valid OAuth access token or API key;
unauthenticated tool calls fail before reaching the RevDesk API.
## Connect
OAuth-capable MCP clients discover RevDesk's authorization server automatically. Connect RevDesk,
select the organization, review the named scopes, and approve the grant. If a client does not
support OAuth, create an API key in **Settings → API Keys**, grant only the scopes the agent needs,
then configure an `Authorization: Bearer rv_…` header:
```json theme={null}
{
"mcpServers": {
"revdesk": {
"type": "http",
"url": "https://mcp.revdesk.com/mcp",
"headers": {
"Authorization": "Bearer ${REVDESK_API_KEY}"
}
}
}
}
```
Keep API keys in your MCP client's secret or environment-variable store. Never paste a key into a prompt,
source file, or configuration file that will be committed.
## Tools
| Tool | Action | Typical scopes |
| - | - | - |
| `send_sms` | Send an SMS from an owned RevDesk number | `sms:write` |
| `place_call` | Place an outbound phone call | `calls:write` |
| `list_calls`, `get_call` | Read call history and details | `calls:read` |
| `hangup_call` | End an in-progress call | `calls:write` |
| `list_phone_numbers` | List numbers available to the organization | `phone_numbers:read` |
| `search_available_numbers` | Search carrier inventory | `phone_numbers:read` |
| `buy_phone_number` | Purchase a number; account billing applies | `phone_numbers:write` |
| `list_caller_ids` | List verified caller IDs and display names | `voice:read` |
| `get_usage` | Read current-month usage | `usage:read` |
The MCP layer calls the same v1 endpoints as the [official TypeScript SDK](/api-reference/sdks).
Scope checks, rate limits, idempotency, and tenant isolation are therefore enforced consistently.
Every tool publishes MCP safety annotations for read-only, destructive, idempotent, and open-world
behavior. `get_usage` also advertises a `ui://` resource using the MCP Apps media type so compatible
hosts can present a compact RevDesk usage surface beside the tool result.
## Security and approvals
Tools that send messages, place calls, buy numbers, or hang up calls change external state or incur
cost. Get the user's approval before placing calls, sending messages, spending money, or changing
configuration. For read-only discovery, use a separate OAuth grant or key containing only the necessary
`:read` scopes.
The protected-resource metadata at
`https://mcp.revdesk.com/.well-known/oauth-protected-resource` lists every supported scope and the
accepted bearer method and OAuth authorization server.
# Number Health and Spam Test sessions
Source: https://docs.revdesk.com/api-reference/number-health
Inspect carrier-specific evidence, organize Number Sets, and persist human spam-test sessions across devices.
Number Health provides the shared evidence behind the app's fleet view and the **revdesk.dev Spam
Test**. A handset observation means “observed on this carrier at this time,” not a guarantee that
a number is clear on every network. Read the server's evidence state and recommended action rather
than calculating readiness or storing authoritative results in localStorage.
The APIs below cover fleet evidence, Number Sets, saved sessions, and audited observations.
They do not yet provide tester-invite redemption, automatic inbound-to-browser handoff, alternate
provisioning, billing resolution, or scheduled retirement. Availability follows the Number Health
rollout; a documented operation does not imply that these additional workflows are enabled.
## Authentication and permissions
Use an organization-scoped bearer credential. Creating sets, updating sets, starting sessions, and
attaching calls also require an associated user for attribution. Keep long-lived API keys on your
server; use the [client-token flow](/api-reference/client-tokens) for browser integrations.
| Operation | Required scope |
| - | - |
| Read policy, fleet, sets, sessions, or inbound evidence; preview set resolution | `caller_trust:read` |
| Create/update sessions, attach attempts, record/correct observations | `caller_trust:write` |
| Create/update Number Sets | `phone_numbers:write` |
| Place a phone-bridge call with `/v1/calls/dial` | `calls:write` |
| Request a human browser call ticket with `/v1/webrtc-token` | `voice:webrtc` |
Responses use `{ "data": ... }`; list responses also include `meta.total_results`. Set, session,
and attempt creation return **201**. Other Number Health operations return **200** on success.
## Read policy and fleet evidence
Fetch `GET /v1/number-health/policy` for the canonical policy version, evidence thresholds, UTC
deduplication rule, and current fresh-check/remediation prices. Use these values in cost previews
and guidance instead of duplicating constants in clients.
`GET /v1/number-health/numbers` returns owned numbers, identity status, provider scores and
freshness, set membership, carrier observations, `health_warning`, and `recommended_action`.
Optional filters are `team_id`, `number_set_id`, `area_code`, and `state`.
| `evidence_state` | Meaning |
| - | - |
| `CLEAN_OBSERVED` | Fresh LOW risk, complete setup, and recent non-negative handset evidence. Not a cross-carrier guarantee. |
| `NEEDS_DATA` | Insufficient or stale evidence. Unknown is not the same as clean. |
| `WATCH` | MEDIUM provider risk requiring monitoring. |
| `FLAGGED` | HIGH risk or recent negative handset evidence, including wrong name, blocked, or no ring. |
| `SETUP_INCOMPLETE` | Missing identity, registration, monitoring, or inbound-routing setup. |
| `RETIRED` | Inactive number or retired set membership; historical evidence is retained. |
Inspect `observations` for `carrier`, `outcome`, `observed_label`, `observed_at`, duration, and
callback evidence. `NUMBER_ONLY` and `LOCATION` are non-spam observations, but do not confirm the
correct clinic name. Null scores or timestamps mean unavailable evidence, not zero risk.
Fleet reads use cached reputation. A paid refresh is a separate explicit
`GET /v1/caller-trust/reputation/numbers/{phone}?fresh=true` request. Remediation uses the existing
Caller Trust remediation API. Preview the cost and obtain approval before issuing paid operations;
creating a test session does not request a fresh reputation check.
## Organize Number Sets
A Number Set groups numbers for one clinic or calling purpose. Area code is only a visual fallback;
sharing an area code does not make two numbers replacements for one another.
Create a set with `POST /v1/number-health/sets`:
```json theme={null}
{
"name": "Downtown clinic",
"use_case": "Appointment confirmations",
"identification_guidance": "Identify the clinic at the start of each call.",
"members": [
{ "phone_id": 101, "role": "PRIMARY", "state": "ACTIVE", "outbound_enabled": true },
{ "phone_id": 102, "role": "ALTERNATE", "state": "PREPARING", "outbound_enabled": false }
]
}
```
Use actual owned phone IDs from the fleet response. `GET /v1/number-health/sets` returns sets and
their members. `PATCH /v1/number-health/sets/{id}` updates fields and upserts supplied members;
omitted members are not removed. To make a member ineligible for selection, set `outbound_enabled`
to `false` or use an ineligible lifecycle state. `RETIRED` is accepted by the update endpoint.
These membership changes affect outbound selection only. They do not buy/release numbers, change
billing, disable inbound service, or delete call history. Marking a member `RETIRED` is not carrier
number retirement.
Preview selection without placing a call:
```http theme={null}
POST /v1/number-health/sets/SET_ID/resolve
Content-Type: application/json
Authorization: Bearer YOUR_SERVER_CREDENTIAL
{ "destination_number": "+12025550123" }
```
The response includes `from_number`, `phone_id`, `evidence_state`, `health_warning`, and
`selection_source: "number_set_sticky_health"`. Selection considers active, outbound-enabled
`ACTIVE`/`STANDBY` members, preferring the healthiest tier, then priority and a recipient-sticky
tie-breaker. Health or membership changes can change the selected caller ID. If all eligible
members are flagged, selection still succeeds with a warning. No eligible member returns **409**.
See [caller-ID selection when dialing](/api-reference/calling-paths#caller-id-selection-and-health-warnings)
for exact-number precedence and the `/v1/calls/dial` request.
## Run a resumable Spam Test
### 1. Create the session
Obtain the tester's consent before submitting their phone number. The consent flag records the
operator's assertion; it is not an OTP verification or an invitation redemption flow.
```http theme={null}
POST /v1/number-health/sessions
Content-Type: application/json
Authorization: Bearer YOUR_SERVER_CREDENTIAL
{
"tester": {
"alias": "Consented mobile tester",
"phone_number": "+12025550123",
"carrier": "TMOBILE",
"consented": true
},
"mode": "RAPID_AUDIT",
"scope": { "kind": "ALL" },
"compare_alternates": true
}
```
Carrier values are `ATT`, `TMOBILE`, `VERIZON`, `OTHER`, and `UNKNOWN`. Scope kinds are `ALL`,
`TEAM`, `NUMBER_SET`, `AREA_CODE`, `FLAGGED`, and `NEEDS_DATA`. For `TEAM`, `NUMBER_SET`, and
`AREA_CODE`, supply the matching identifier or three-digit area code in `scope.value`.
| Mode | Purpose |
| - | - |
| `RAPID_AUDIT` | Quick carrier-specific handset audit. Does not count toward readiness. |
| `HUMAN_READINESS` | Qualifying human evidence across testers, days, and carriers. |
| `CONNECTIVITY_QA` | Routing/audio/lifecycle checks; synthetic evidence never counts toward readiness. |
The returned `data.id` identifies the saved session. `GET /v1/number-health/sessions/{id}` returns
its selected `phone_ids` and attempts. Use those server records to resume on another device.
`PATCH` the same URL with `{ "status": "PAUSED" }`, `ACTIVE`, `COMPLETED`, or `CANCELLED` to
change its lifecycle without deleting evidence. The selected queue is saved when the session starts.
### 2. Place the label-test call
For a **Quick fleet scan**, call the tester directly from the candidate number using the human
browser WebRTC flow below. Inbound checks are optional, and a label audit has no 60-second target.
There is no need to sit through an AI or prerecorded greeting to unlock the outbound test.
For **Human Readiness**, have the tester call a human-handled candidate RevDesk number first. Read
`GET /v1/number-health/sessions/{id}/inbound?phone_id=101` for server-confirmed progress:
`call_id`, `status`, `answered_at`, `ended_at`, `duration_seconds`, `callback_verified`, and `human`.
Only calls from this tester to a queued number after session creation are considered.
This endpoint observes the number's existing inbound routing. It does not redirect the call to
the browser operator. Arrange human inbound handling before a readiness test. An AI answering
an inbound call is not qualifying human callback evidence.
For a human browser callback, use the existing [WebRTC flow](/api-reference/webrtc-security) with
the exact candidate `from_number` and the consented tester as `to_number`. No assigned AI agent is
required. Disable duplicate placement while a call request is pending.
Attach the resulting call to the session using `POST /v1/number-health/sessions/{id}/attempts`:
```json theme={null}
{ "phone_id": 101, "call_id": "CALL_ID_FROM_THE_CALL_FLOW" }
```
This endpoint attaches an existing call; it does not dial. The server checks ownership, queue
membership, session timing, and tester destination. Human modes reject AI calls. Wait for carrier
completion events before recording the observation; a browser stopwatch is not duration evidence.
### 3. Record exactly what appeared
Submit to `POST /v1/number-health/attempts/{id}/observation`:
```json theme={null}
{
"outcome": "SPAM",
"observed_label": "Spam likely",
"handset": { "manufacturer": "Apple", "model": "iPhone", "os": "iOS" }
}
```
Outcomes are `CLEAN` (correct name), `NUMBER_ONLY`, `LOCATION`, `WRONG_NAME`, `SPAM`, `SCAM`,
`BLOCKED`, `NO_RING`, or `UNKNOWN`. Supply the exact display text, not a guessed carrier verdict.
The server measures durations and derives callback evidence from actual calls. Do **not** set
`callback_verified: true` automatically: that optional field is an explicit, audited operator
attestation. Reposting a corrected observation preserves a before/after audit snapshot.
If the call has not reached a terminal carrier state, the endpoint returns **400** with retry
guidance. After saving, reload the session and fleet for evidence eligibility, exclusion reasons,
and the next recommended action. Save the session ID; localStorage is not the results database.
## Readiness is evidence, not a warm-up guarantee
The current policy targets 8 qualifying human calls, 5 consented testers, 4 active days, and a
60-second conversational duration. Readiness also needs clean observations on AT\&T, T-Mobile,
and Verizon, a verified callback, LOW provider risk, and no recent negative display.
Only one qualifying number/tester interaction per UTC calendar day contributes to readiness.
There is no 48-hour rejection of legitimate extra calls. These are RevDesk evidence criteria,
not a carrier promise that call length or a fixed number of calls removes spam labels. Do not
disguise synthetic calls or automate repetitive warm-up loops. The QA mode records exclusion;
it is not a dedicated AI/prerecorded scenario runner.
## Divide testing across the team
Staff can start separate sessions for their own consented handset and carrier, scope the scan by
area code or evidence state, and select the numbers they actually use from the queue. In Spam Test,
**Leave untested and next** moves to another number without inventing an observation or marking the
skipped number clean. Skipped numbers remain untested when the saved session is resumed.
If a readiness session encounters an automated inbound route, **Set up Quick fleet scan instead**
preserves the old session and opens setup for a new label-only audit. It does not convert synthetic
calls into human readiness evidence. An authenticated shared session link resumes the same tester's
session; it is not an invitation for a different handset or carrier.
# Attach a real call to a Number Health session
Source: https://docs.revdesk.com/api-reference/number-health/attach-a-real-call-to-a-number-health-session
/openapi.json post /v1/number-health/sessions/{id}/attempts
Creates an audited attempt from an existing human WebRTC call. Server call events remain authoritative for answer state and duration.
# Create a Number Set
Source: https://docs.revdesk.com/api-reference/number-health/create-a-number-set
/openapi.json post /v1/number-health/sets
Creates an organization-scoped clinic/purpose set from owned phone numbers.
# Get the canonical Number Health evidence policy
Source: https://docs.revdesk.com/api-reference/number-health/get-the-canonical-number-health-evidence-policy
/openapi.json get /v1/number-health/policy
Returns readiness criteria and current wallet prices for explicit fresh-check/remediation previews.
# List authoritative number-health evidence
Source: https://docs.revdesk.com/api-reference/number-health/list-authoritative-number-health-evidence
/openapi.json get /v1/number-health/numbers
Returns every phone number in the workspace with Number Set membership, provider scores, carrier-specific handset observations, evidence freshness, and the next recommended action.
# List Number Sets
Source: https://docs.revdesk.com/api-reference/number-health/list-number-sets
/openapi.json get /v1/number-health/sets
Lists explicit clinic/purpose caller-ID sets and their current and alternate members.
# Pause, resume, or close a Number Health session
Source: https://docs.revdesk.com/api-reference/number-health/pause-resume-or-close-a-number-health-session
/openapi.json patch /v1/number-health/sessions/{id}
Updates session lifecycle without deleting its saved observations or call evidence.
# Read carrier-confirmed inbound test progress
Source: https://docs.revdesk.com/api-reference/number-health/read-carrier-confirmed-inbound-test-progress
/openapi.json get /v1/number-health/sessions/{id}/inbound
Only returns calls from the session's consented tester to a number in its saved queue, after the session began.
# Record or revise a carrier/handset observation
Source: https://docs.revdesk.com/api-reference/number-health/record-or-revise-a-carrierhandset-observation
/openapi.json post /v1/number-health/attempts/{id}/observation
Stores the exact handset display with server-measured call evidence and an audit snapshot.
# Resolve a Number Set caller ID
Source: https://docs.revdesk.com/api-reference/number-health/resolve-a-number-set-caller-id
/openapi.json post /v1/number-health/sets/{id}/resolve
Chooses a recipient-sticky outbound member from the healthiest available tier, with warnings.
# Resume a Number Health test session
Source: https://docs.revdesk.com/api-reference/number-health/resume-a-number-health-test-session
/openapi.json get /v1/number-health/sessions/{id}
Returns the saved session scope, tester, queue, and server-backed attempts.
# Start a resumable Number Health test session
Source: https://docs.revdesk.com/api-reference/number-health/start-a-resumable-number-health-test-session
/openapi.json post /v1/number-health/sessions
Saves a consented tester, test mode, and immutable selected-number queue for a workspace.
# Update a Number Set and its member roles
Source: https://docs.revdesk.com/api-reference/number-health/update-a-number-set-and-its-member-roles
/openapi.json patch /v1/number-health/sets/{id}
Updates explicit clinic membership, roles, and outbound eligibility. This changes outbound selection only; it does not disable inbound service or alter billing.
# Get a number registration
Source: https://docs.revdesk.com/api-reference/number-registration/get-a-number-registration
/openapi.json get /v1/number-registrations/{id}
One registration with its latest submission state and per-engine acknowledgments (hiya, tns, first_orion). A registration is `submitted` once the registry accepts it and `active` after the first engine acknowledges — propagation to carrier spam labeling typically completes within 24–48 hours.
# Get the workspace's Free Caller Registry registration profile
Source: https://docs.revdesk.com/api-reference/number-registration/get-the-workspaces-free-caller-registry-registration-profile
/openapi.json get /v1/caller-trust/registration-profile
Returns the workspace's default call purpose and monthly call-volume bracket submitted to the Free Caller Registry, the effective values in use today, and the full lists of valid options.
# List number registrations
Source: https://docs.revdesk.com/api-reference/number-registration/list-number-registrations
/openapi.json get /v1/number-registrations
RevDesk-managed Free Caller Registry status for hosted numbers in your workspace, oldest first and cursor-paged. Filter by status or an exact phone_number.
# Set the workspace's Free Caller Registry registration profile
Source: https://docs.revdesk.com/api-reference/number-registration/set-the-workspaces-free-caller-registry-registration-profile
/openapi.json put /v1/caller-trust/registration-profile
Sets the default call purpose and monthly call-volume bracket for Free Caller Registry submissions. call_purpose and monthly_call_volume must be option values from GET (call_purpose_options / monthly_call_volume_options).
# Optimize
Source: https://docs.revdesk.com/api-reference/optimize
Run website tests from your own systems: create tests, read results, record conversions, and hear about winners over webhooks.
Optimize runs A/B tests on your website through one tag. The API lets you do from your
own systems everything the app does: create a test on a page, start and pause it, read its
results, ship the winner, and record conversions the tag cannot see (a closed order, a signed
contract). Webhooks tell you when a test starts, finds a winner, or ships.
## Install the tag
```bash theme={null}
curl https://api.revdesk.com/v1/optimize/snippet \
-H "Authorization: Bearer $REVDESK_API_KEY"
# → { "data": { "property_id": "…", "public_key": "…", "script_tag": "" } }
```
Paste `script_tag` into the `` of every page you want to test. The property is verified the
first time the tag loads from the registered domain; `GET /v1/optimize/properties` shows `verified_at`.
## Endpoints
All endpoints take the `optimize:read` scope for reads and `optimize:write` for changes.
| Method | Path | What it does |
| - | - | - |
| `GET` | `/v1/optimize/tests` | List tests, filter by `status`, page with `limit` and `offset`. |
| `POST` | `/v1/optimize/tests` | Create a test on one page with variants; `start: true` starts it. |
| `GET` | `/v1/optimize/tests/{id}` | One test with its goal and variants. |
| `PATCH` | `/v1/optimize/tests/{id}` | Edit name, hypothesis, page, traffic split; `status` starts/pauses. |
| `DELETE` | `/v1/optimize/tests/{id}` | Archive. Results and shipped changes are kept. |
| `POST` | `/v1/optimize/tests/{id}/ship` | Make a variant permanent and retire the test. |
| `GET` | `/v1/optimize/tests/{id}/results` | Per-variant impressions, conversions, lift, significance, value. |
| `GET` | `/v1/optimize/conversions` | Conversions recorded for a test. |
| `POST` | `/v1/optimize/conversions` | Record a conversion from your own systems. |
| `GET` | `/v1/optimize/properties` | The workspace's websites and whether the tag has been seen. |
| `GET` | `/v1/optimize/snippet` | The script tag to install. |
## Create and start a test
A test is a page, a goal, and one or more variants. Each variant is a list of DOM operations the
tag applies for the visitors bucketed into it: `setText`, `setAttribute`, `addClass`,
`removeClass`, `setStyle`, `hide`, and `replaceHref`. Nothing in the list can introduce script.
```bash theme={null}
curl -X POST https://api.revdesk.com/v1/optimize/tests \
-H "Authorization: Bearer $REVDESK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Pricing headline",
"page_url": "/pricing",
"goal": { "type": "form_submit", "selector": "form#lead" },
"variants": [
{ "name": "Outcome first", "ops": [{ "op": "setText", "selector": "h1", "value": "Close more deals this month" }] }
],
"start": true
}'
```
Goal types: `form_submit`, `tel_click`, `cta_click`, `selector_click` (each takes a `selector`),
and `url_visit` (takes `url_match_type` and `url_pattern`). `traffic_split` (0.05 to 1) is the
share of visitors enrolled; the rest see the page untouched.
## Record a conversion
The tag counts goals it can see on the page. For outcomes that happen elsewhere, send them in.
Include the tag's `visitor_id` (the `rd_sid` cookie, or `window.rdSite.visitorId`) so the
conversion is credited to the arm that visitor saw.
```bash theme={null}
curl -X POST https://api.revdesk.com/v1/optimize/conversions \
-H "Authorization: Bearer $REVDESK_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "test_id": "…", "visitor_id": "0123456789abcdef0123456789abcdef", "value": 249.99, "metadata": { "order_id": "ord_42" } }'
# → { "data": { "recorded": true, "test_id": "…", "variant_id": "…" } }
```
`metadata.order_id` makes the write idempotent: a repeat returns `recorded: false`. `value` is in
major units and defaults to USD; it shows up as `revenue_cents` per variant in results.
## Read results
```bash theme={null}
curl https://api.revdesk.com/v1/optimize/tests/{id}/results \
-H "Authorization: Bearer $REVDESK_API_KEY"
```
`variants[]` carries `impressions`, `conversions`, `conversion_rate`, `lift`, `p_value`,
`probability_to_beat_control`, and `revenue_cents`. `decision.reason` is one of `winner`,
`collecting`, `no_difference`, or `inconclusive`; `decision.winner_variant_id` is set only for
`winner`. `daily[]` is a per-variant, per-day series for charts.
## Events
Subscribe with [webhooks](/api-reference/webhooks). Site events are delivered flat and carry the
event name in the body:
| `event_type` | Fires when |
| - | - |
| `optimize.test.created` | A test was created. |
| `optimize.test.started` | A test started or resumed serving variants. |
| `optimize.test.completed` | A test finished without a winner being declared. |
| `optimize.test.winner` | A test finished with a winner. `test.winner_variant_id` names it. |
| `optimize.test.shipped` | A variant was made permanent on the page. |
| `optimize.conversion.created` | A conversion was recorded through the API. |
```json theme={null}
{
"event": "optimize.test.winner",
"occurred_at": "2026-09-09T12:00:00.000Z",
"test": {
"id": "…",
"name": "Pricing headline",
"status": "completed",
"page_url": "/pricing",
"winner_variant_id": "…",
"variants": [{ "id": "…", "name": "Control", "is_control": true, "weight": 1, "ops": [], "redirect_url": null }]
}
}
```
`optimize.conversion.created` carries `conversion` instead of `test`, in the same shape as
`GET /v1/optimize/conversions`.
# Archive a website test
Source: https://docs.revdesk.com/api-reference/optimize/archive-a-website-test
/openapi.json delete /v1/optimize/tests/{id}
Archives a test. A running test stops serving its variants; results and shipped changes are kept. Tests are never hard deleted.
# Create a website test
Source: https://docs.revdesk.com/api-reference/optimize/create-a-website-test
/openapi.json post /v1/optimize/tests
Creates a test on one page with a control plus the variants you send. Each variant is a list of DOM operations (text, attributes, classes, inline style, hide, link targets) applied by the tag. Pass a `goal` to create one or `goal_id` to reuse one. With `start: true` the test begins immediately; otherwise it is saved as a draft.
# Get a website test
Source: https://docs.revdesk.com/api-reference/optimize/get-a-website-test
/openapi.json get /v1/optimize/tests/{id}
Returns a single website test with its goal and variants.
# Get the site tag
Source: https://docs.revdesk.com/api-reference/optimize/get-the-site-tag
/openapi.json get /v1/optimize/snippet
Returns the script tag for the workspace's website. Install it in the head of every page; the property is verified the first time the tag loads from the registered domain.
# Get website test results
Source: https://docs.revdesk.com/api-reference/optimize/get-website-test-results
/openapi.json get /v1/optimize/tests/{id}/results
Per-variant impressions, conversions, lift, significance, and conversion value, with the current decision and a daily series. Results are recomputed when the stored rollup is more than five minutes old.
# List conversions for a website test
Source: https://docs.revdesk.com/api-reference/optimize/list-conversions-for-a-website-test
/openapi.json get /v1/optimize/conversions
Returns the conversions recorded for a test, newest first: those the tag observed on the page and those sent to POST /v1/optimize/conversions.
# List site properties
Source: https://docs.revdesk.com/api-reference/optimize/list-site-properties
/openapi.json get /v1/optimize/properties
Returns the websites registered to the workspace with their public tag key and whether the tag has been seen on the site. A workspace has one website today.
# List website tests
Source: https://docs.revdesk.com/api-reference/optimize/list-website-tests
/openapi.json get /v1/optimize/tests
Returns the workspace's website tests, newest first. Filter by `status` and page with `limit` and `offset`. `property_id` is optional while a workspace has one website.
# Record a conversion
Source: https://docs.revdesk.com/api-reference/optimize/record-a-conversion
/openapi.json post /v1/optimize/conversions
Records a conversion for a test from your own systems (a closed order, a booked appointment, a signed contract) so the results reflect outcomes the tag cannot see. Send the tag's `visitor_id` so the conversion is credited to the arm that visitor saw; a conversion with neither `variant_id` nor a known `visitor_id` is stored but cannot count toward an arm. Repeat sends with the same `metadata.order_id` are ignored.
# Ship a website test
Source: https://docs.revdesk.com/api-reference/optimize/ship-a-website-test
/openapi.json post /v1/optimize/tests/{id}/ship
Makes a variant permanent for every visitor of the page and retires the test. Send `{}` to ship the recorded winner, or `variant_id` to choose. A running test is completed first.
# Update a website test
Source: https://docs.revdesk.com/api-reference/optimize/update-a-website-test
/openapi.json patch /v1/optimize/tests/{id}
Changes a test's name, hypothesis, page, or traffic split, and starts or pauses it through `status`. Fields left out are unchanged. Shipped and archived tests cannot be edited.
# Check live CNAM status
Source: https://docs.revdesk.com/api-reference/phone-numbers/check-live-cnam-status
/openapi.json get /v1/phone-numbers/{id}/cnam-status
Queries the carrier for the current CNAM status. A pending name is promoted to APPROVED only when the carrier reports that same name as enabled.
# List owned phone numbers
Source: https://docs.revdesk.com/api-reference/phone-numbers/list-owned-phone-numbers
/openapi.json get /v1/phone-numbers
Returns every Phone row owned by the caller's active organization.
# Purchase a new phone number
Source: https://docs.revdesk.com/api-reference/phone-numbers/purchase-a-new-phone-number
/openapi.json post /v1/phone-numbers
Initiates the durable provisioning workflow. Returns 202 with a workflow run id — poll `/v1/phone-numbers/{phone_record_id}` to see when the number goes active. The new number attaches to the caller's active organization.
# Release a phone number
Source: https://docs.revdesk.com/api-reference/phone-numbers/release-a-phone-number
/openapi.json delete /v1/phone-numbers/{id}
Releases the phone number from the carrier, cancels the Stripe subscription line item, deletes associated call data and recordings, and removes the database record.
# Retrieve a phone number
Source: https://docs.revdesk.com/api-reference/phone-numbers/retrieve-a-phone-number
/openapi.json get /v1/phone-numbers/{id}
Polls for status. While provisioning is in flight, `is_active=false` and `phone_number` is a `pending:…` placeholder. Once the workflow completes, both fields update atomically.
# Search available phone numbers
Source: https://docs.revdesk.com/api-reference/phone-numbers/search-available-phone-numbers
/openapi.json get /v1/phone-numbers/search
Query carrier inventory for numbers matching the given filters. Returns up to 50 results per call.
# Update a phone number's settings
Source: https://docs.revdesk.com/api-reference/phone-numbers/update-a-phone-numbers-settings
/openapi.json patch /v1/phone-numbers/{id}
Mutates name + recording flags + CNAM display name. CNAM is the caller-ID name shown on outbound calls (max 15 chars, US local numbers only). Setting `cnam_display_name` updates the carrier on the spot — propagation to carriers takes 24-72h.
# Quickstart
Source: https://docs.revdesk.com/api-reference/quickstart
Place your first call with the RevDesk API in about five minutes.
This walks you from zero to a live, connected call. You'll authenticate, place a call between two
numbers, and check its outcome.
**Prefer TypeScript?** The official [`@revdesk/sdk`](/api-reference/sdks) wraps every endpoint below
with typed methods, so `revdesk.calls.dial({ … })` replaces the raw `curl` calls. See
[Official SDKs](/api-reference/sdks).
Create a key in **Settings → API Keys** (or ask your RevDesk contact). Keys are prefixed with
`rv_` and are shown once. Store it as an environment variable:
```bash theme={null}
export REVDESK_API_KEY="rv_your_key_here"
```
Every request authenticates with a Bearer token. See [Authentication](/api-reference/authentication)
for scopes.
Calls go out *as* a number on your account. List yours:
```bash theme={null}
curl https://api.revdesk.com/v1/phone-numbers \
-H "Authorization: Bearer $REVDESK_API_KEY"
```
Note one active number to use as your caller ID (`from_number`). Don't have one yet? See
use `POST /v1/phone-numbers` in the [API Reference](/api-reference) to provision one.
Bridge your number to a destination. The called party sees your `from_number`:
```bash theme={null}
curl -X POST https://api.revdesk.com/v1/calls/dial \
-H "Authorization: Bearer $REVDESK_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"from_number": "+14155550123",
"to_number": "+14155559876"
}'
```
The response returns immediately with a `call_id` and `status: "QUEUED"`. The call is placed
asynchronously.
Poll the call to watch it progress (`QUEUED → RINGING → IN_PROGRESS → COMPLETED`) and get the
duration and recording when it ends:
```bash theme={null}
curl https://api.revdesk.com/v1/calls/CALL_ID \
-H "Authorization: Bearer $REVDESK_API_KEY"
```
## Where to go next
Browser calling vs. connecting on a phone, and which fits your use case.
Text from your numbers and stream inbound replies.
Verify caller IDs, set CNAM, and register your brand.
Errors, idempotency, and pagination shared by every endpoint.
# Disable Number Reputation monitoring
Source: https://docs.revdesk.com/api-reference/reputation/disable-number-reputation-monitoring
/openapi.json delete /v1/caller-trust/reputation
Disables reputation monitoring for the caller's enterprise. Only works when the current status is APPROVED. All enrolled numbers will stop being monitored.
# Disenroll a phone number from reputation monitoring
Source: https://docs.revdesk.com/api-reference/reputation/disenroll-a-phone-number-from-reputation-monitoring
/openapi.json delete /v1/caller-trust/reputation/numbers/{phone}
Removes a phone number from carrier number-reputation monitoring. The number must be currently enrolled. Cached reputation scores are deleted.
# Enable Number Reputation monitoring
Source: https://docs.revdesk.com/api-reference/reputation/enable-number-reputation-monitoring
/openapi.json post /v1/caller-trust/reputation
Enables (or re-enables) enterprise-level reputation monitoring via the carrier's reputation provider. Requires an enterprise that has been submitted to the carrier (has a carrier_enterprise_id) and has an LOA document on file. Idempotent on REJECTED status — calling this after rejection replaces the rejected record with a fresh PENDING one.
# Enroll phone numbers in reputation monitoring
Source: https://docs.revdesk.com/api-reference/reputation/enroll-phone-numbers-in-reputation-monitoring
/openapi.json post /v1/caller-trust/reputation/numbers
Enrolls one or more phone numbers in carrier number-reputation monitoring. Numbers must belong to the caller and be in US E.164 format. Enrollment is idempotent — re-enrolling an already-enrolled number is a no-op.
# Get a remediation request
Source: https://docs.revdesk.com/api-reference/reputation/get-a-remediation-request
/openapi.json get /v1/caller-trust/reputation/remediations/{id}
Status and per-number outcomes for one remediation request. Outcomes are null until processing completes (usually a few days). Possible outcomes: remediated, not_flagged, requires_review, ineligible, refused.
# Get Number Reputation enrollment settings
Source: https://docs.revdesk.com/api-reference/reputation/get-number-reputation-enrollment-settings
/openapi.json get /v1/caller-trust/reputation
Number reputation monitoring is included for every number under RevDesk's shared umbrella. This returns per-enterprise settings (status + check frequency) only for own-brand registrations; it returns null when the caller's numbers are on the shared umbrella (the default), which is not an error.
# Get reputation scores for a phone number
Source: https://docs.revdesk.com/api-reference/reputation/get-reputation-scores-for-a-phone-number
/openapi.json get /v1/caller-trust/reputation/numbers/{phone}
Returns Hiya-sourced spam risk + 0-100 component scores. Passing `fresh=true` forces a live carrier query billed at $0.25 to your wallet (rate-limited to 1/day/number). Cached reads are free — the weekly scheduled check keeps them warm.
# List remediation requests
Source: https://docs.revdesk.com/api-reference/reputation/list-remediation-requests
/openapi.json get /v1/caller-trust/reputation/remediations
Every remediation request that includes at least one of your numbers, newest first. Use GET /remediations/{id} for per-number outcomes.
# List reputation scores for every enrolled number
Source: https://docs.revdesk.com/api-reference/reputation/list-reputation-scores-for-every-enrolled-number
/openapi.json get /v1/caller-trust/reputation/numbers
Returns per-phone spam risk + component scores for every number the caller owns that's enrolled under an enterprise (either umbrella or own-brand).
# Submit numbers for spam-label remediation
Source: https://docs.revdesk.com/api-reference/reputation/submit-numbers-for-spam-label-remediation
/openapi.json post /v1/caller-trust/reputation/remediations
Asks the carrier analytics providers to clear spam/scam-likely labels on the given numbers. Billed $2 per number to your wallet once accepted, regardless of outcome. Limit: one remediation per number per 14 days. Numbers must be enrolled in reputation monitoring. Async — poll GET /remediations/{id} or subscribe to the reputation.remediation_completed webhook.
# Update reputation check frequency
Source: https://docs.revdesk.com/api-reference/reputation/update-reputation-check-frequency
/openapi.json patch /v1/caller-trust/reputation
Changes how often the carrier refreshes spam scores for enrolled numbers. Each refresh is billed per number, so this is a cost lever. Only works when reputation status is APPROVED.
# RevDesk SDK & CLI
Source: https://docs.revdesk.com/api-reference/sdks
Official typed TypeScript packages and command-line tooling for the RevDesk API and browser calling.
RevDesk ships two official packages so you don't have to hand-roll HTTP calls or wire up browser
audio:
Typed REST client for the v1 API: calls, SMS, phone numbers, caller IDs, caller trust, usage, and
sub-entities. Types are generated from the OpenAPI spec, so they never drift from the API.
Browser-calling client. Place and receive calls in the browser through the RevDesk voice network, with one
branded surface for both connection paths.
The REST SDK is open source on
[GitHub](https://github.com/cell-labs-inc/revdesk-sdk). Its generated types come from RevDesk's
[public OpenAPI 3.1 schema](https://www.revdesk.com/openapi.json).
## @revdesk/sdk
### Install
```bash theme={null}
npm install @revdesk/sdk
# or: bun add @revdesk/sdk
```
### Command line
`@revdesk/sdk` includes the official `revdesk` CLI for scripts and one-off API requests:
```bash theme={null}
export REVDESK_API_KEY="rv_…"
npx @revdesk/sdk request GET /v1/me
npx @revdesk/sdk request GET '/v1/calls?limit=10'
```
Run `npx @revdesk/sdk --help` for all options. The CLI requires `/v1/*` paths and sends the key only
through the `Authorization` header.
### Quickstart
```ts theme={null}
import { RevDesk } from "@revdesk/sdk";
const revdesk = new RevDesk({ apiKey: process.env.REVDESK_API_KEY! });
// List phone numbers
const { data: numbers } = await revdesk.phoneNumbers.list({ limit: 25 });
// Place an outbound call
await revdesk.calls.dial({ from_number: "+15551230000", to_number: "+15554560000" });
// Send an SMS
await revdesk.sms.send({ from_number: "+15551230000", to_number: "+15554560000", body: "Hi!" });
```
### AI agents
Every number you buy gets a RevDesk AI agent wired up to answer it automatically, through the
dashboard or the API, the same provisioning path, no extra setup. The `agents` resource lets you
discover, configure, assign, and call those agents from code.
```ts theme={null}
// List the agents in your workspace (and which number each answers)
const { data: agents } = await revdesk.agents.list();
// Configure one: greeting, instructions, voice, language, on/off
await revdesk.agents.update(agents[0].id, {
greeting: "Thanks for calling Acme — how can I help?",
system_prompt: "You are Acme's assistant. Book appointments and answer questions.",
voice_id: "aoede",
language: "en",
});
// Reassign which agent answers an inbound number
await revdesk.phoneNumbers.update(phoneId, { agent_id: agents[0].id });
// Point an outbound call at a specific agent
await revdesk.calls.create({ phoneNumber: "+15554560000", assistantId: agents[0].id });
// Talk to an agent straight from the browser, with no phone number needed
const { data: call } = await revdesk.agents.webCall(agents[0].id);
// → { token, room_url, … } hand to @revdesk/webrtc's RevDeskRoom to join
```
Reading agents needs the `agents:read` scope; configuring, assigning, and `webCall` need `agents:write`.
Configuring an agent requires a user-scoped key.
### Bounded client tokens
Mint a short-lived, number-restricted token on your **server**, then hand it to an untrusted client
(mobile app, browser) in place of your API key. The client can only call within the from/to numbers
you choose. See [Bounded client tokens](/api-reference/client-tokens) for the full model.
```ts theme={null}
// On your server (holds the API key):
const { data } = await revdesk.clientTokens.create({
from_numbers: ["+15551234567"], // caller IDs you own
to_numbers: ["+15557654321"], // optional destination allowlist
ttl_seconds: 900,
});
// Hand data.token (an rdc_… string) to your client.
// In the client, construct the SDK with the minted token instead of the API key:
const client = new RevDesk({ apiKey: data.token });
await client.webrtc.getToken({ from_number: "+15551234567", to_number: "+15557654321" });
```
Minting requires the `tokens:mint` scope. A minted token cannot mint further tokens.
### Pagination
Cursor-paginated endpoints expose `listAll()`, an async iterator that walks every page for you:
```ts theme={null}
for await (const number of revdesk.phoneNumbers.listAll()) {
console.log(number.phone_number);
}
```
`listAll()` is available on `phoneNumbers`, `agents`, `subEntities`, and `callerTrust.reputation.numbers`.
### Idempotency
Mutating calls accept an `idempotencyKey` so a retried request is processed once:
```ts theme={null}
import { generateIdempotencyKey } from "@revdesk/sdk";
await revdesk.sms.send(
{ from_number: "+15551230000", to_number: "+15554560000", body: "Hi!" },
{ idempotencyKey: generateIdempotencyKey() }
);
```
### Errors
Every non-2xx response throws a typed `RevDeskError`:
```ts theme={null}
import { RevDeskError } from "@revdesk/sdk";
try {
await revdesk.calls.dial({ from_number: "+1555…", to_number: "+1555…" });
} catch (err) {
if (err instanceof RevDeskError) {
console.error(err.code, err.status, err.message);
if (err.isRetryable) {
/* rate-limited or 5xx, safe to retry */
}
}
}
```
See [Errors](/api-reference/errors) for the full `RevDeskError` shape.
## @revdesk/webrtc
### Install
```bash theme={null}
npm install @revdesk/webrtc
# or: bun add @revdesk/webrtc
```
Issue the call token server-side with `@revdesk/sdk`, then hand it to the browser client. Both
clients emit the same `callUpdate` / `error` / `ended` events, so your UI doesn't change when you
switch paths. See [Choosing a calling path](/api-reference/calling-paths) for the differences.
### Browser-direct — `RevDeskRTC`
```ts theme={null}
import { RevDeskRTC } from "@revdesk/webrtc";
// token from revdesk.webrtc.getToken({ from_number, to_number })
const call = new RevDeskRTC({ token });
call.on("callUpdate", ({ state }) => console.log(state));
await call.connect();
call.dial({ to: "+15554560000", from: "+15551230000" });
call.sendDTMF("1"); // out-of-band IVR input; not mixed into microphone audio
```
Handle `reconnecting` as an in-progress call: the client is restoring media after a network or
native call-audio interruption. Do not tear the call down; it returns to `active` when audio is ready.
During initial setup, recoverable signaling interruptions also emit `reconnecting`. Keep waiting
for `connect()` before dialing; concurrent calls to `connect()` share the same pending connection.
The original `connectTimeoutMs` deadline still applies (15 seconds by default), and authentication
failures reject immediately. Timeout stops the underlying connection.
Calling `disconnect()` during setup rejects the pending promise with `connection_canceled`, returns
the state to `idle`, and prevents a later timeout error. This cancellation does not emit a call-failure
event. Close unused server-side tickets separately; see [WebRTC security](/api-reference/webrtc-security).
### Relay — `RevDeskRoom`
```ts theme={null}
import { RevDeskRoom } from "@revdesk/webrtc";
// from revdesk.webrtc.getRoomToken({ from_number, to_number })
const { token, room_url, call_id } = await revdesk.webrtc.getRoomToken({ from_number, to_number });
const call = new RevDeskRoom({ token, roomUrl: room_url, callId: call_id });
call.on("callUpdate", ({ state }) => console.log(state));
await call.join();
```
For relay calls, `active` means the destination answered. Stop local ringback on that state.
Audio playback permission is separate: subscribe to `audioPlaybackChanged` before `join()` and
show an "Enable call audio" button when it reports `false`. Call `call.startAudio()` directly
from the button's click/tap handler, catching rejection if playback remains blocked. A playback
block does not end the call. Read `call.canPlaybackAudio` for the current permission state.
Calls fail through the `error` event with a typed `RevDeskCallError` (`code` is one of `connection_failed`,
`connection_timeout`, `call_rejected`, …).
# Get a number's SMS auto-reply override
Source: https://docs.revdesk.com/api-reference/sms/get-a-numbers-sms-auto-reply-override
/openapi.json get /v1/phone-numbers/{id}/sms-config
Returns the per-number SMS auto-reply override, if any. When `has_override` is false the number inherits the workspace setting (GET /v1/sms/config).
# Get an SMS message
Source: https://docs.revdesk.com/api-reference/sms/get-an-sms-message
/openapi.json get /v1/messages/{id}
Returns a single SMS message by id (the id returned from list and send responses).
# Get workspace SMS auto-reply settings
Source: https://docs.revdesk.com/api-reference/sms/get-workspace-sms-auto-reply-settings
/openapi.json get /v1/sms/config
Returns the workspace-wide SMS auto-reply configuration. `auto_reply_enabled: false` is one-way SMS — inbound texts are still received, the AI just doesn't auto-respond. Per-number overrides are managed on /v1/phone-numbers/{id}/sms-config.
# List SMS messages
Source: https://docs.revdesk.com/api-reference/sms/list-sms-messages
/openapi.json get /v1/messages
Returns recent SMS messages for the authenticated organization, newest first. Direction is derived per message: inbound messages came from the contact's number, outbound messages were sent from one of your numbers.
# Send an SMS message
Source: https://docs.revdesk.com/api-reference/sms/send-an-sms-message
/openapi.json post /v1/sms/send
Sends an outbound SMS from the specified phone number. The 'from' number must be an active phone number owned by the authenticated organization.
# Set a number's SMS auto-reply override
Source: https://docs.revdesk.com/api-reference/sms/set-a-numbers-sms-auto-reply-override
/openapi.json patch /v1/phone-numbers/{id}/sms-config
Creates or updates a per-number SMS auto-reply override, taking precedence over the workspace setting. Set `auto_reply_enabled: false` for one-way SMS on this number only.
# GET /v1/sms/stream
Source: https://docs.revdesk.com/api-reference/sms/stream
GET https://api.revdesk.com/v1/sms/stream
Subscribe to real-time inbound SMS events via Server-Sent Events (SSE).
Opens a long-lived Server-Sent Events (SSE) connection that pushes inbound SMS messages to your client in real time. Messages are scoped to the phone numbers owned by the authenticated API key.
## Authentication
Requires a Bearer API key with the `sms:read` scope.
```bash theme={null}
curl -N "https://api.revdesk.com/v1/sms/stream" \
-H "Authorization: Bearer YOUR_API_KEY"
```
## Events
### `connected`
Sent immediately after the connection is established.
```
event: connected
data: {"clientId":"sms-42-1716000000000-abc1234","timestamp":1716000000000}
```
### `sms_received`
Sent when an inbound SMS arrives on one of your active phone numbers.
```
event: sms_received
data: {"message_id":"msg_abc123","from":"+14155551234","to":"+14155559999","body":"Hello!","received_at":"2026-05-22T18:30:00.000Z"}
```
| Field | Type | Description |
| - | - | - |
| `message_id` | string | Unique identifier for the message |
| `from` | string | Sender phone number (E.164) |
| `to` | string | Your phone number that received the message (E.164) |
| `body` | string | Message text content |
| `received_at` | string | ISO 8601 timestamp of when the message was received |
### Heartbeat
A comment-only heartbeat is sent every 30 seconds to keep the connection alive:
```
: heartbeat
```
## Connection behavior
* **Max duration:** 300 seconds (5 minutes). Reconnect after the connection closes.
* **Reconnect:** Re-open the stream (with a short backoff) after it closes. Do **not** use native
`EventSource` — it cannot send an `Authorization` header, and you must never put your API key in
the URL (it leaks into logs and history). Use `fetch` with a streamed body reader instead.
* **Scope filtering:** Events are filtered to the user or team associated with the API key.
**Consume this stream from your server, never from browser JavaScript.** It needs a full API key,
which is long-lived and carries every scope the key was issued with. Anything you ship to a browser
is readable by an extension, a devtools pane, a bundled source map, or any XSS on the page, and a
leaked key is usable until you notice and revoke it. The stream also carries full phone numbers and
message bodies, so the exposure is your customers' data, not just your credential.
Connect from a backend process and relay what the browser actually needs over your own
authenticated channel (a WebSocket or your own SSE endpoint), filtered to what that user may see.
[Client tokens](/api-reference/client-tokens) are not an alternative here: they only ever carry
`voice:webrtc`, never `sms:read`.
## Example
```javascript Node (fetch stream) theme={null}
// Runs on your server. Native EventSource can't set headers, so use fetch + a stream reader.
const res = await fetch("https://api.revdesk.com/v1/sms/stream", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
for (;;) {
const { value, done } = await reader.read();
if (done) break; // connection closed — reconnect with a short backoff
buffer += decoder.decode(value, { stream: true });
// SSE frames are separated by a blank line.
const frames = buffer.split("\n\n");
buffer = frames.pop() ?? "";
for (const frame of frames) {
const event = frame.match(/^event: (.+)$/m)?.[1];
const dataLine = frame.match(/^data: (.+)$/m)?.[1];
if (!dataLine) continue; // e.g. `: heartbeat`
const data = JSON.parse(dataLine);
if (event === "sms_received") console.log(`New SMS from ${data.from}: ${data.body}`);
}
}
```
```python Python theme={null}
import requests
import json
url = "https://api.revdesk.com/v1/sms/stream"
headers = {"Authorization": "Bearer YOUR_API_KEY"}
with requests.get(url, headers=headers, stream=True) as r:
for line in r.iter_lines(decode_unicode=True):
if line.startswith("data: "):
data = json.loads(line[6:])
print(f"Received: {data}")
```
# Update workspace SMS auto-reply settings
Source: https://docs.revdesk.com/api-reference/sms/update-workspace-sms-auto-reply-settings
/openapi.json patch /v1/sms/config
Updates the workspace-wide SMS auto-reply configuration. Set `auto_reply_enabled: false` for one-way SMS (the AI stops auto-responding to inbound texts).
# Create a sub-entity under the caller's umbrella organization
Source: https://docs.revdesk.com/api-reference/sub-entities/create-a-sub-entity-under-the-callers-umbrella-organization
/openapi.json post /v1/sub-entities
Creates a new managed child organization. When the key carries a user, that user becomes the owner. HIPAA posture is INHERITED from the umbrella (fail-closed): if the umbrella is HIPAA-enabled the new sub-entity starts HIPAA-enabled.
# List sub-entities under the caller's umbrella organization
Source: https://docs.revdesk.com/api-reference/sub-entities/list-sub-entities-under-the-callers-umbrella-organization
/openapi.json get /v1/sub-entities
Every managed child organization (sub-entity) of the authenticated org. Each entry includes member + phone-number counts so the caller can tell which sub-entities are live.
# Retrieve a sub-entity
Source: https://docs.revdesk.com/api-reference/sub-entities/retrieve-a-sub-entity
/openapi.json get /v1/sub-entities/{id}
Retrieve one child organization that the authenticated umbrella organization can administer, including its HIPAA posture and current member and phone-number counts.
# Update a sub-entity
Source: https://docs.revdesk.com/api-reference/sub-entities/update-a-sub-entity
/openapi.json patch /v1/sub-entities/{id}
Rename or flip the `hipaa_enabled` flag. Changing `hipaa_enabled` requires the `compliance:write` scope IN ADDITION to `sub_entities:write`. HIPAA is fail-closed and non-downgradeable: `hipaa_enabled=false` is REJECTED while the umbrella organization is HIPAA-enabled. Enabling HIPAA defaults the sub-entity's call-recording retention to 30 days (adjustable per number afterward).
# Create a task
Source: https://docs.revdesk.com/api-reference/tasks/create-a-task
/openapi.json post /v1/tasks
Hand RevDesk an outcome to pursue. The agent plans and executes across channels until the success criteria are met, the deadline passes, or the guardrails stop it.
# Create a task (legacy path)
Source: https://docs.revdesk.com/api-reference/tasks/create-a-task-legacy-path
/openapi.json post /v1/jobs
Deprecated compatibility alias for `POST /v1/tasks`. Hand RevDesk an outcome to pursue; progress and the terminal outcome arrive through task webhooks.
# List tasks
Source: https://docs.revdesk.com/api-reference/tasks/list-tasks
/openapi.json get /v1/tasks
Tasks the organization has open or has completed, newest first. Filter with `status` (OPEN, PAUSED, ACHIEVED, ABANDONED, EXPIRED).
# List tasks (legacy path)
Source: https://docs.revdesk.com/api-reference/tasks/list-tasks-legacy-path
/openapi.json get /v1/jobs
Deprecated compatibility alias for `GET /v1/tasks`. Tasks are returned newest first and may be filtered with `status` (OPEN, PAUSED, ACHIEVED, ABANDONED, EXPIRED).
# Retrieve a task
Source: https://docs.revdesk.com/api-reference/tasks/retrieve-a-task
/openapi.json get /v1/tasks/{taskId}
The task, every band of its provenance timeline in order, and each recorded outcome with the interactions that produced it.
# Retrieve a task (legacy path)
Source: https://docs.revdesk.com/api-reference/tasks/retrieve-a-task-legacy-path
/openapi.json get /v1/jobs/{jobId}
Deprecated compatibility alias for `GET /v1/tasks/{taskId}`. Returns the task, its provenance timeline, and recorded outcomes with evidence.
# Current-month usage rollup
Source: https://docs.revdesk.com/api-reference/usage/current-month-usage-rollup
/openapi.json get /v1/usage
Aggregated counts across all numbers the key can see: outbound calls, minutes used, and spam-risk distribution across enrolled numbers.
# Per-phone usage
Source: https://docs.revdesk.com/api-reference/usage/per-phone-usage
/openapi.json get /v1/usage/{phoneId}
Returns voice usage for a specific phone number in the current calendar month. The phone must belong to the authenticated user or team.
# Versioning and deprecation
Source: https://docs.revdesk.com/api-reference/versioning-and-deprecation
How RevDesk versions its public API and communicates incompatible changes.
RevDesk versions its public REST API in the URL. The current stable surface is `/v1`. Additive
changes—new endpoints, optional fields, enum values, and response properties—may ship within the
current version. Clients should ignore response properties they do not recognize.
## Compatibility policy
Within a stable API version, RevDesk does not remove documented fields, change their meaning, or
make an optional request field required without a deprecation period. Security fixes may tighten
validation immediately when accepting the old input would expose customers or third parties to
material risk.
## Deprecation notice
The `/v1/jobs` collection and detail routes are deprecated aliases for `/v1/tasks`. They remain
available without a scheduled removal date, so existing integrations continue to work while new
integrations use the task routes. Deprecated aliases return a `Deprecation` header and a
`successor-version` link.
If an incompatible removal becomes necessary, RevDesk will:
1. Mark the affected OpenAPI operation or field as `deprecated`.
2. Publish the replacement and migration instructions in these docs.
3. Return a `Deprecation` response header and a `Link` header with `rel="deprecation"` from the
affected endpoint.
4. When a retirement date is known, return a `Sunset` header and provide at least 90 days' notice
before disabling the old behavior.
## Monitoring changes
Use the canonical [OpenAPI document](https://www.revdesk.com/openapi.json) for automated contract
checks. The [official TypeScript SDK](/api-reference/sdks) is generated from that document and
follows semantic versioning.
# RevDesk Webhooks
Source: https://docs.revdesk.com/api-reference/webhooks
Subscribe to RevDesk call, SMS, contact, number-lifecycle, registration, booking, task, and website-test events delivered to your URL.
RevDesk POSTs an event to your URL whenever something happens in your workspace: a call ends, an SMS
is delivered, a number is provisioned. Subscribe once per event type; we deliver as the events occur.
## Subscribe
```bash theme={null}
curl -X POST https://api.revdesk.com/v1/webhook_subscriptions \
-H "Authorization: Bearer $REVDESK_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "webhook_url": "https://example.com/hooks/revdesk", "event_type": "call_completed" }'
# → { "data": { "id": "whk_…", "secret": "<64-char hex>" } }
# `secret` signs every delivery (X-RevDesk-Signature) and is shown only here — store it now.
```
| Field | Required | Description |
| - | - | - |
| `webhook_url` | yes | HTTPS URL that receives the `POST`. |
| `event_type` | yes | One of the event types below. |
## List subscriptions
```bash theme={null}
curl https://api.revdesk.com/v1/webhook_subscriptions \
-H "Authorization: Bearer $REVDESK_API_KEY"
# → { "data": [ { "id": "whk_…", "webhook_url": "…", "event_type": "call_completed", "active": true, "created_at": "…" } ], "meta": { … } }
# Signing secrets are shown once at creation and are never returned here.
```
## Unsubscribe
```bash theme={null}
curl -X DELETE https://api.revdesk.com/v1/webhook_subscriptions/whk_… \
-H "Authorization: Bearer $REVDESK_API_KEY"
# → { "data": { "id": "whk_…", "deleted": true } }
```
## Event types
`call_completed` fires on every call end. The **disposition** events below fire *in addition*, only on the
matching outcome. Subscribe to them to branch (retry, SMS fallback) without parsing call records.
Answered/successful calls emit no disposition event.
| `event_type` | Fires when |
| - | - |
| `new_call` | A call starts. |
| `call_completed` | A call ends (any outcome). |
| `call_no_answer` | A call ended unanswered (rang out). |
| `call_voicemail` | A call reached voicemail / an answering machine. |
| `call_busy` | The destination was busy. |
| `call_failed` | A call failed (declined, unreachable, carrier rejection). |
| `contact_replied` | An inbound SMS was received. |
| `sms_sent` | An outbound SMS was accepted by the carrier. |
| `sms_delivered` | An outbound SMS was delivered to the handset. |
| `sms_failed` | An outbound SMS failed / was undelivered. |
| `contact_created` | A new contact was created. |
| `number_purchased` | A phone number finished provisioning and went active. |
| `number_released` | A phone number was released from your workspace. |
| `number_updated` | A phone number's settings changed (name, CNAM, recording, agent assignment, …). |
| `texting_registration_status_changed` | Your A2P texting registration (brand or campaign) was approved or rejected by carriers. On campaign approval the payload lists `texting_enabled_numbers`, the moment to start sending. |
| `new_appointment` | A booking was created. |
| `registration_submitted` | A [number registration](/concepts/number-registration) was submitted to the carrier. |
| `registration_confirmed` | A number registration was accepted. |
| `registration_failed` | A number registration was rejected. The payload carries the reason. |
| `job_started` | A [Task](/concepts/tasks) opened a pursuit. The legacy event name is retained for compatibility. |
| `job_progressed` | A task took an action. Carries `channel`, `action`, and the `rationale` behind it. |
| `job_outcome_recorded` | A task recorded an outcome, with the `evidence` behind it. |
| `job_completed` | A task reached a terminal status. |
| `optimize.test.created` | A [website test](/api-reference/optimize) was created. |
| `optimize.test.started` | A website test started or resumed serving variants. |
| `optimize.test.completed` | A website test finished without a winner being declared. |
| `optimize.test.winner` | A website test finished with a winner. The payload's `test.winner_variant_id` names it. |
| `optimize.test.shipped` | A website test's variant was made permanent on the page. |
| `optimize.conversion.created` | A conversion was recorded for a website test through the API. |
## Delivery
Each event is a `POST` with a JSON body to your `webhook_url`. Delivery is fire-and-forget; return a
`2xx` quickly and do any heavy work asynchronously. Subscribe to the same event type multiple times
to fan out to multiple URLs.
**Two delivery shapes.** Call-disposition, SMS, and task events are delivered **flat** (the event
object *is* the request body). Number-lifecycle events are delivered **enveloped** (`triggerEvent` /
`createdAt` / `payload`). Both shapes are **signed**. Match the shape to the event family you
subscribed to (see the examples below), but verify the signature the same way for all of them.
Flat deliveries carry no event-name field. Branch on the payload instead: call and SMS events on
`status`, task events on `status` plus the fields unique to each (`job_progressed` carries
`rationale`, `job_outcome_recorded` carries `outcome`). The one exception is the website test
family: `optimize.*` deliveries are flat and carry the event name in `event`.
### Signature verification (all events)
Every subscription has a signing secret (generated at creation, shown once; rotate it to get a new
one). Every delivery carries three headers:
| Header | Meaning |
| - | - |
| `X-RevDesk-Event-Id` | Unique id for this event. **Dedupe on it.** A delivery with an id you have already processed is a retry or a replay; ignore it. |
| `X-RevDesk-Timestamp` | Unix seconds when the event was signed. Reject anything outside a few minutes of your clock. |
| `X-RevDesk-Signature` | `v2=`. During a secret rotation a second comma-separated `v2=` value (signed with the previous secret) rides alongside, so a delivery verifies if **either** matches. |
Signing over `{eventId}.{timestamp}.{rawBody}` rather than the body alone is what makes a captured
request non-replayable. Both moving parts are inside the signature: the timestamp can't be moved
forward, and the event id can't be swapped. That second property is what lets you dedupe safely. If
the id weren't signed, anyone who captured one delivery could resend it with a fresh id inside the
tolerance window and your dedupe would wave it through as a new event.
```js theme={null}
import crypto from "node:crypto";
// Verify a RevDesk webhook. Returns true only for a fresh, correctly-signed request.
function verifyWebhook(rawBody, headers, secret, { toleranceSeconds = 300 } = {}) {
const timestamp = Number(headers["x-revdesk-timestamp"]);
if (!Number.isFinite(timestamp)) return false;
// Replay defense: reject stale (or future-dated) deliveries.
if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > toleranceSeconds) return false;
// The event id is part of the signed material, so verify against the id as received.
const eventId = headers["x-revdesk-event-id"];
if (!eventId) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${eventId}.${timestamp}.${rawBody}`)
.digest("hex");
const expectedBuf = Buffer.from(expected);
// Accept any of the comma-separated v2 signatures (handles secret rotation).
return (
(headers["x-revdesk-signature"] ?? "")
.split(",")
.map((s) => s.trim())
.filter((s) => s.startsWith("v2="))
.map((s) => Buffer.from(s.slice(3)))
// Length-check BEFORE timingSafeEqual, since it throws on unequal-length buffers.
.some((sig) => sig.length === expectedBuf.length && crypto.timingSafeEqual(sig, expectedBuf))
);
}
```
**Verify first, then dedupe on `X-RevDesk-Event-Id`** (e.g. a short-lived set or a unique column) so a
retried delivery is processed at most once. The order matters: the id only means something once the
signature covering it has checked out.
**Legacy header (deprecated).** Older integrations verified an `X-revdesk-Signature-256` /
`X-RevDesk-Signature-256` header, a bare HMAC of the body with no timestamp. It still ships
alongside the new headers during a deprecation window but offers no replay protection at all;
migrate to the `X-RevDesk-Signature` verifier above.
**`v1=` signatures are retired.** A short-lived earlier version of `X-RevDesk-Signature` signed
`{timestamp}.{rawBody}`, leaving the event id unauthenticated. It is no longer sent. If your
verifier filters for the `v1=` prefix, switch it to `v2=` and add the event id to the signed
string as shown above.
#### Rotating a signing secret
Rotate from the dashboard or the API to get a fresh secret (shown once). For a grace period RevDesk
signs each delivery with **both** the new and previous secret, so you can deploy the new secret
without dropping in-flight events; once your consumers use the new secret, the old one stops signing.
### Payload examples
Call disposition (`call_no_answer`, `call_voicemail`, `call_busy`, `call_failed`) — **flat** (signed like every event):
```json theme={null}
{
"id": "call_abc123",
"from_number": "+15551234567",
"to_number": "+15557654321",
"direction": "outbound",
"duration_seconds": 0,
"status": "VOICEMAIL",
"failure_code": "voicemail_reached",
"failure_category": "voicemail",
"disconnection_reason": null,
"created_at": "2026-06-30T19:30:00.000Z"
}
```
Outbound SMS (`sms_sent`, `sms_delivered`, `sms_failed`) — **flat** (signed like every event):
```json theme={null}
{
"id": "msg_uuid",
"phone_number": "+15557654321",
"from_number": "+15551234567",
"direction": "outbound",
"status": "delivered",
"contact_token": "…",
"provider_id": "…",
"error_message": null,
"channel": "sms",
"created_at": "2026-06-30T19:30:00.000Z"
}
```
Task progress (`job_started`, `job_progressed`, `job_outcome_recorded`, `job_completed`) — **flat**
(signed like every event). The `job_*` event names and `job_id` field are stable legacy contract
names. Every task event carries the five base fields; `job_progressed` adds the
action and the reasoning behind it:
```json theme={null}
{
"job_id": "job_abc123",
"type": "lease_end_retention",
"status": "OPEN",
"objective": "Book a test drive with Dana Whitfield",
"contact_id": "c_123",
"task_id": "task_789",
"channel": "IMESSAGE",
"action": "send_message",
"rationale": "Replied on iMessage twice and never answered a call, so text rather than dial."
}
```
Website tests (`optimize.test.created`, `optimize.test.started`, `optimize.test.completed`, `optimize.test.winner`,
`optimize.test.shipped`) are delivered **flat**, with the event name in `event` and the test in `test`, in the same
shape as `GET /v1/optimize/tests/{id}`. `optimize.conversion.created` carries `conversion` instead:
```json theme={null}
{
"event": "optimize.test.winner",
"occurred_at": "2026-09-09T12:00:00.000Z",
"test": {
"id": "…",
"name": "Pricing headline",
"status": "completed",
"page_url": "/pricing",
"winner_variant_id": "…",
"variants": [{ "id": "…", "name": "Control", "is_control": true, "weight": 1, "ops": [], "redirect_url": null }]
}
}
```
Number lifecycle (`number_purchased`, `number_released`, `number_updated`) — **enveloped**. The
event data is nested under `payload`, and `triggerEvent` is the internal event name
(`number.purchased` / `number.released` / `number.updated`):
```json theme={null}
{
"triggerEvent": "number.purchased",
"createdAt": "2026-06-30T19:30:00.000Z",
"payload": {
"id": 4821,
"phone_number": "+15551234567",
"area_code": "555",
"status": "ACTIVE",
"organization_id": "…"
}
}
```
`number.updated` additionally carries `changed_fields` — the snake\_case names of the settings the
update touched (e.g. `["cnam_display_name", "agent_id"]`).
Signed like every other event: verify `X-RevDesk-Signature` with the
[verifier above](#signature-verification-all-events). Only the body shape differs.
## Receiving leads
Webhook subscriptions deliver RevDesk events **out** to your systems. To send a new lead **into**
RevDesk, create a lead source in **Directory → Lead sources** and use its source-specific
`POST /api/webhooks/leads/{intakeToken}` endpoint. Lead intake uses its own rotatable URL token and
secret; it does not use an API key or a webhook-subscription signing secret. See
[Tasks and lead sources](/getting-started/features/tasks-and-lead-sources#generic-webhook) for the
payload, authentication, field mapping, response codes, health checks, and rotation procedure.
# Create a webhook subscription
Source: https://docs.revdesk.com/api-reference/webhooks/create-a-webhook-subscription
/openapi.json post /v1/webhook_subscriptions
Subscribes an https URL to an event type (one of: new_call, call_completed, contact_replied, contact_created, new_appointment, number_purchased, number_released, number_updated, call_no_answer, call_voicemail, call_busy, call_failed, sms_sent, sms_delivered, sms_failed, texting_registration_status_changed, registration_submitted, registration_confirmed, registration_failed, job_started, job_progressed, job_outcome_recorded, job_completed, optimize.test.created, optimize.test.started, optimize.test.completed, optimize.test.winner, optimize.test.shipped, optimize.conversion.created). The response includes the HMAC signing secret used for X-RevDesk-Signature — it is shown once, here, and is not retrievable afterwards.
# Delete a webhook subscription
Source: https://docs.revdesk.com/api-reference/webhooks/delete-a-webhook-subscription
/openapi.json delete /v1/webhook_subscriptions/{id}
Deletes a webhook subscription. Pending deliveries for it are cancelled.
# List webhook subscriptions
Source: https://docs.revdesk.com/api-reference/webhooks/list-webhook-subscriptions
/openapi.json get /v1/webhook_subscriptions
Returns the webhook subscriptions created through this API for the authenticated organization. Signing secrets are shown once at creation and are never returned here.
# WebRTC security model
Source: https://docs.revdesk.com/api-reference/webrtc-security
Scoped single-use tokens, owned caller ID, account guardrails, and how to get a hard destination lock.
Browser calling uses a **scoped, single-use token** instead of a general-purpose calling credential.
This page is precise about what each path guarantees, so you can match it to your risk tolerance.
There are two tiers of enforcement:
* **Every path** issues scoped, short-lived, single-use tokens, validates the caller ID against
numbers you own, attributes every call, and bounds the blast radius with account guardrails.
* **The server-originated paths** (join + bridge) additionally **lock the destination by
construction**: the client never dials, so the call can't be redirected or replayed.
## How a token is issued and bounded (all paths)
```mermaid theme={null}
sequenceDiagram
participant App as Your app
participant API as RevDesk API
participant SDK as @revdesk/webrtc
App->>API: POST /v1/webrtc-token { from_number, to_number }
API->>API: validate you OWN from_number (caller ID)
API->>API: check usage limits + account guardrails
API->>API: mint per-call credential (short TTL) + record the ticket (org, user, from→to)
API-->>App: opaque token (rvw_…) + call_id
App->>SDK: new RevDeskRTC({ token }), then connect() + dial()
Note over App,SDK: token is single-use, valid for that one call only
```
The token you receive is an **opaque RevDesk token** (`rvw_…`), not a reusable calling credential. It
is issued for one call, tied to a caller ID you own, and recorded for attribution.
On the **browser-direct** path the destination is **declared, attributed, and guardrail-bounded**, but the
browser is what dials, so the destination is not *locked by construction*. For a hard destination lock, use
a server-originated path (below).
## Canceling abandoned browser setup
Keep the `call_id` returned by `POST /v1/webrtc-token`. If browser setup fails, your
server can send `POST /v1/calls/{id}/hangup?mode=setup_failed`. When the user cancels
before dialing, use `mode=cancel_setup`. These requests require `calls:write` and
no request body; a browser-only `voice:webrtc` client token cannot call this endpoint.
Never expose your server API key to the browser.
Both setup modes act only on unused, queued browser tickets. They revoke the ticket
and mark it `CANCELED`, with the setup diagnostic recorded separately and no verified talk time. A browser error or missing webhook does not prove the handset failed to ring. They do not
interrupt an originated call, even if origination races with cancellation. Repeating
the request on a terminal call returns its current status. Omit `mode` (or use
`mode=hangup`) to end an actual call as usual.
An unused ticket also receives a five-minute server-side cleanup deadline. This is
an abandonment timeout, not an active-call duration limit: originated calls retain
their normal credential lifetime. Failed setup is not a completed conversation,
readiness evidence, or a spam-label observation. Retry explicitly with a new ticket;
the system does not redial automatically.
Call lifecycle and recipient outcome are separate. A finished attempt can have
`status: "completed"` and `call_outcome: "screened"` when the recipient explicitly
reported screening it (`disconnection_reason: "call_screened"`). This does not
establish a human conversation: `picked_up` is false, and unknown timings stay
null. Abandoned browser setup uses `call_outcome: "connection_unconfirmed"`, not
an inferred carrier failure or spam label.
## Hard destination lock: server-originated paths
On the **join** and **bridge** paths, RevDesk's server places the call and the client only joins it.
The destination is fixed server-side, so it **cannot be changed by the client and the token cannot be
replayed to reach a different number.**
```mermaid theme={null}
sequenceDiagram
participant App as Your app
participant RD as RevDesk (server)
participant Dest as Called party
App->>RD: request a call to to_number (from a number you own)
RD->>Dest: server places the call (caller ID = your owned number)
RD-->>App: client joins the call it set up
Note over App,Dest: client never dials, destination is fixed server-side
```
### Bridge: caller-ID masking
When a person connects on their own phone (bridge), the called party only ever sees your RevDesk
number, never the person's personal number.
```mermaid theme={null}
sequenceDiagram
participant App as Your app
participant RD as RevDesk
participant Agent as Your phone
participant Dest as Called party
App->>RD: POST /v1/calls/dial { from_number, to_number, connect_number }
RD->>Agent: ring connect_number (caller ID = from_number)
RD->>Dest: call to_number (caller ID = from_number)
Note over Dest: sees from_number only, never connect_number
Agent-->>RD: answers → bridged with called party
```
## Account guardrails (defense in depth, all paths)
These cap the blast radius even if a token were leaked:
```mermaid theme={null}
flowchart TB
s1["Scope: a key limited to voice:webrtc"] --> s2["Ownership: caller ID must be a number you own"]
s2 --> s3["Per-call token: single use, one call only"]
s3 --> s4["Account profile: destination allowlist · daily spend cap · concurrency cap · max rate"]
```
## Security questions, answered
Yes. Every token is issued for one `from_number` → `to_number`, is single-use, and is valid only for that
one call. It's invalidated when the call ends (its lifetime covers the platform's max call duration so it
never cuts an active call). On the browser-direct path the destination is declared, attributed, and
bounded by the account guardrails. For a destination that **cannot** be changed or replayed, use a
server-originated path (join or bridge), where the client never dials.
Yes. `from_number` must be a number your organization owns; we validate ownership at issuance and only
mint a token for a number you control.
A destination allowlist (blocks international/premium ranges), a daily spend limit, a concurrency cap, and
a max per-minute rate, all enforced at the account level on every call, so a leaked token's blast radius
is bounded.
Every call is anchored to a record carrying your organization, the issuing user, and the from/to.
Reconcile with `GET /v1/calls`.
No, by design. A number anyone can dial can't authenticate the caller, lock the destination, or mask
caller ID per call. The bridge path places both legs from the server instead: you call `POST
/v1/calls/dial`, RevDesk rings your phone and the called party, then bridges them. That server-placed
model is what makes per-call attribution and caller-ID masking possible. For a phone (cellular, no
browser), bridge is the path; see [Choosing a calling path](/api-reference/calling-paths).
It's single-use (one call only) and guardrail-bounded. To make a token **non-replayable for origination**,
usable only through a call RevDesk sets up, use a server-placed path (B or C): the client holds no dialing
credential at all, only a grant to join a call RevDesk placed. See [Choosing a calling
path](/api-reference/calling-paths).
# Issue a room-join token for private browser calling
Source: https://docs.revdesk.com/api-reference/webrtc/issue-a-room-join-token-for-private-browser-calling
/openapi.json post /v1/room-token
Issues a single-call token for browser calling where RevDesk places the call and your browser joins it. You provide a caller ID (from_number, which must be a number you own) and a destination (to_number); RevDesk dials the destination and returns a token your browser uses to join the live call and talk through the mic. Nothing about the underlying network is exposed to the browser. The connection runs entirely on RevDesk's own infrastructure. Pass the returned token and room_url to the RevDesk browser SDK (@revdesk/webrtc) to join.
# Issue a scoped WebRTC calling token
Source: https://docs.revdesk.com/api-reference/webrtc/issue-a-scoped-webrtc-calling-token
/openapi.json post /v1/webrtc-token
Issues a single-use WebRTC token for the RevDesk WebRTC SDK, pinned to one caller ID (from_number, which must be a number you own) and one destination (to_number). The token is an opaque RevDesk token — pass it to the @revdesk/webrtc SDK to place exactly that one call. It is valid only for that single call and is invalidated when the call ends; it cannot be reused. (Token lifetime covers the platform max call duration so it never cuts an active call.)
# Mint a bounded client token
Source: https://docs.revdesk.com/api-reference/webrtc/mint-a-bounded-client-token
/openapi.json post /v1/client-tokens
Issues a short-lived, downscoped token (rdc_…) that is safe to ship into an untrusted client app. The token is restricted to the from_numbers (caller IDs) and to_numbers (destinations) you specify, so it can only place calls within those bounds — never from any number to any number. Present it on the same Authorization: Bearer header in place of your API key; the WebRTC SDK accepts it directly. The token's bounds must fall within this key's own number allowlist and its scopes; it is stateless and revoked by expiry. This endpoint requires a real API key — a minted token cannot mint further tokens. Regardless of the parent key's scopes, a client token may only carry the browser-calling scope (voice:webrtc) — it can never make durable account changes or mint further tokens. An explicit, non-empty to_numbers (destination allowlist) is required so the token can never dial an arbitrary number.
# Hire the agents you need
Source: https://docs.revdesk.com/concepts/agents
An agent is an AI teammate that owns one stage of your customer lifecycle. Hire one, name it, give it a voice, and it starts answering calls and working leads.
An **agent** is an AI teammate that owns one stage of your customer lifecycle. You hire one the way you would hire a person: pick the role, give it a name and a voice, answer a few questions about how you work, and it starts.
Each one owns a stage. Answer & Route answers, Lead Response reaches new leads, Customer Success follows up after the job. They share one contact record and one calendar, so a lead that comes in on the phone and books by text is one conversation, not three.
## The six roles
Every workspace starts with Answer & Route. It is the floor: it answers so nothing is missed. The other five are specialists you add as you need them.
| Agent | What it owns | What you measure |
| - | - | - |
| **Answer & Route** | Answers every call, 24/7, qualifies leads and books meetings. | Calls answered, bookings made |
| **Sales Assist** | Auto-dials for your reps and coaches every call, with analytics. | Conversations connected, flagged-call rate |
| **Lead Response** | Reaches new leads fast, on the channels you allow. | Median time to first response, leads booked |
| **Prospecting** | Works your net-new prospect lists and follows up with every prospect. | Conversations held, meetings booked |
| **Customer Success** | Follows up after every job, and turns customers into reviews and referrals. | Follow-ups completed, reviews earned |
| **Re-Engage** | Wins back customers and turns them into repeat revenue. | No-shows recovered, customers reactivated |
They group by what they do for you:
* **Core:** Answer & Route, Sales Assist
* **Win new business:** Lead Response, Prospecting
* **Keep and grow customers:** Customer Success, Re-Engage
The systems enabled for a workspace determine which roles and actions are available. Naming or configuring a
agent does not add a capability that is not enabled for the workspace.
## Templates
A template is a working agent you can start from instead of a blank page. It carries a greeting, a conversation flow, the tools the agent can call, and a sample of how it sounds on a real call.
| Template | Role | Good for |
| - | - | - |
| **Front desk** | Answer & Route | Answers every call, books the ones worth booking, routes the rest. |
| **New lead caller** | Lead Response | Reaches a new lead fast, qualifies, and books before they cool off. |
| **Service scheduler** | Answer & Route | Takes service requests and gets the vehicle in. Automotive workspaces. |
| **After-hours cover** | Answer & Route | Picks up when you have closed, and only wakes someone for a real emergency. |
| **Win-back caller** | Re-Engage | Calls customers who went quiet and finds out what it would take. |
| **Review and referral asker** | Customer Success | Follows up after the job, asks for the review, and catches problems first. |
Browse them under **Agents → Templates**, filtered by industry and use case. Picking one seeds a new agent with its greeting and flow already filled in. You can change anything afterwards.
## Hiring one
From **Agents**, choose a role card, or open **Templates** and pick a starting point.
Each role asks what it needs to do the job. Answer & Route asks for your business hours and a transfer
number. Lead Response asks where your leads come from. Prospecting asks you to attest that every contact
has prior express written consent.
Pick the voice, the model tier, and the language. Write the greeting and the personality prompt, or keep
the template's.
The agent is created and its workflows are provisioned. It shows **Setup needed** until everything it
depends on is connected, then flips to **Active**.
## What you can change
| | |
| - | - |
| **Name** | What the agent is called, in the product and on the call. |
| **Business name and description** | Who it is speaking for. |
| **Greeting** | The opening line. Supports `{{businessName}}`, `{{agentName}}`, and `{{contactName}}`. |
| **Personality** | The system prompt: tone, rules, what to never say. |
| **Voice** | Any voice in the catalogue, with a model tier and a language. |
| **Role setup answers** | The per-role questions above. |
The role itself is fixed once hired, because it is what the agent is. To change the job, hire the agent that does it.
## Running more than one of a role
You can run several agents in the same role, for example two Sales Assist agents covering different teams. One of them is the **primary** for that role, and it is the one that carries the subscription. Additional agents in a role you already hold do not add to your bill.
## Firing one
Removing an agent is scheduled, not immediate. It keeps working until the end of the billing period, and re-hiring it before then costs nothing. Answer & Route cannot be removed while you hold specialists, since it is what answers the phone.
***
## One shared record
Your agents work from one contact record, and the data layer keeps it complete.
**Audience building.** Define the eligible audience, exclusions, owning system, and required context before a
agent receives work. See [Build and enrich an audience](/guides/workflows/build-and-enrich-an-audience).
**Enrichment.** When enabled and supported by the source, RevDesk can add context needed by the next approved
action. Treat enrichment as context, not proof of identity, eligibility, or consent.
Because the record is shared, a prospect gets one consistent treatment. The data layer enriches the record before anyone touches it. An agent reaches out on the channel most likely to get an answer. If the prospect replies by text instead of picking up, the thread continues without re-asking what they already said. When intent crosses your threshold, the meeting lands on the right rep's calendar with a structured note, a recording, and a transcript.
## Boundaries
Agents operate inside well-defined limits.
Answer questions from your knowledge base
Book meetings against the right rep's calendar
Qualify against your scoring criteria
Transfer to a human when intent spikes or the conversation calls for it
Follow up by voice, SMS, email, or another call
Speculate when the knowledge base doesn't have the answer
Promise terms outside your stated policies
Claim to be human when asked directly
Surface or call data we do not have rights to
Handle scenarios you've routed to escalation
When a conversation goes outside the knowledge base, the agent captures contact details, hands off cleanly, and flags the gap in your dashboard so you can teach the team for next time.
## Teaching the team
You teach your agents once, in one place. The same knowledge base, qualification rules, ICP definition, and brand voice apply to all of them.
Budget, timeline, fit, decision-maker, use case. Agents apply these in conversation and adapt to each
caller rather than reading a rigid script.
Pricing, product specs, common objections, policies, services. See [Knowledge
base](/concepts/knowledge-base).
Hand-off lands in HubSpot or LeadConnector with structured fields populated.
Bookings land on the right rep's calendar.
Every conversation produces a transcript and a summary. Gaps surface in the dashboard, and you teach your
agents from them.
**AI Agents and runtime assistants.** The agents you configure in the product provision the runtime assistants exposed by the API.
`GET /v1/agents` returns **assistants**, which are the runtime voices attached to your phone numbers and campaigns. They are not the same objects as the agents you hire on this page, and the counts will not match. An agent provisions the workflows and assistants that do its work.
The scopes are `agents:read` and `agents:write`. When you are reading [the API reference](/api-reference), the `/agents` resource refers to runtime assistants.
Hand your agents an outcome and let them work every task until it happens.
What your agents know about your business.
Pick the voice and write the personality.
Launch a controlled inbound, outbound, lead, or retention workflow.
# Call Quality & Carriers
Source: https://docs.revdesk.com/concepts/call-quality-carriers
How RevDesk's carrier partnerships make calls recognizable, keep audio clean, and keep per-minute pricing low.
## Why call quality is a business problem
Two things decide whether a dial becomes a conversation: the network carrying the audio, and how the receiving phone labels the call. Most AI voice products lease capacity through reseller SIP providers, which means thinner margins, worse routing, and a caller ID that shows up as "Scam Likely."
RevDesk doesn't route through middlemen. We run on direct tier-1 carrier partnerships, which changes what customers actually get.
***
## What you get from tier-1 partnerships
Direct carrier relationships cut out reseller markup. [Contact sales](https://cal.com/revdesk/30min) for
AI voice and human-connected talk time pricing.
Your name can replace "Unknown" on supported handsets.
Carrier-side reputation management means fewer spam flags, shorter post-dial delays, and more calls that
actually complete.
RevDesk manages codec compatibility automatically for each carrier path. Wideband audio is used only on
routes where it is stable, with a compatible fallback when it is not.
***
## Branded Calling, explained
Branded Calling registers your display identity (name, logo, call reason) with the engines that label calls. It needs your own brand registration.
The visible result on the receiver's phone:
* Before: `+1 (415) 555-0100` or `Scam Likely`
* After: `Acme Roofing` (with a verified checkmark on carriers that support it)
Branded Calling is a RevDesk add-on. Contact sales for pricing. We handle registration with the major U.S. carriers and keep it in sync as you add numbers or rotate them.
See [Phone Numbers](/getting-started/setup/phone-numbers#branded-caller-id) for how numbers are provisioned and how Branded Calling is set up.
***
## Compliance is part of the infrastructure
Carrier partnerships are how we enforce TCPA and state rules at the network layer, not as an afterthought.
* **DNC scrubbing** runs before every dial against the federal registry and your opt-out list.
* **State calling-hour rules** apply automatically (no Sunday dials in Alabama, 8pm cutoff in Florida, and so on).
* **Consent and recording disclosures** are part of the agent script and the carrier's audit log.
* **Full audit trail** per contact: opt-in source, timestamps, consent status, recording.
[Read more in Compliance](/getting-started/features/compliance).
***
## Geographic coverage
Local presence in 14+ countries, including the United States, Canada, the United Kingdom, Ireland, Australia, New Zealand, Germany, France, the Netherlands, Spain, Italy, Sweden, Finland, and Denmark. Branded Calling and caller registration cover US numbers today.
Need coverage somewhere we don't list? [Contact us](https://cal.com/revdesk/30min) — most new markets take a week or two to add.
***
## What this means for your numbers
| | Reseller SIP (most AI vendors) | RevDesk (tier-1 direct) |
| - | - | - |
| Per-minute pricing | \$0.15–\$0.40 | Contact sales |
| Caller ID | Number only, or "Unknown" | Your business name (Branded Calling) |
| Spam labeling | Common | Monitored daily; not guaranteed |
| Audio quality | Standard; often compressed | Carrier-aware codec routing |
| DNC / TCPA enforcement | App-layer, best-effort | Network-layer, automatic |
| Setup | Bring your own registrations | Included and managed |
***
Register your business name with every major U.S. carrier.
Provision new numbers or port existing ones.
# Caller trust and number reputation
Source: https://docs.revdesk.com/concepts/caller-trust
Number reputation and branded calling, the two carrier-level features that keep outbound calls out of spam-filter purgatory.
## What Caller Trust covers
Caller Trust is the RevDesk name for two features that ride on the same carrier primitive and decide whether your outbound calls get picked up. **Number Reputation** tracks live spam scores per number, daily. **Branded Calling** puts your business name, logo, and call reason on the recipient's screen instead of a raw number.
Both run on a tier-1 carrier's Enterprise primitive, which requires a Federal EIN and a signed Letter of Authorization for the legal entity whose brand is on the line. RevDesk wraps that in a two-tier model so you don't have to handle EIN-and-LOA on day one.
## Two tiers
For fleet-level evidence, current/alternate Number Sets, and resumable handset tests, use
[Number Health and Spam Test sessions](/api-reference/number-health). Number Health combines
cached provider reputation with identity setup and carrier-specific observations; a single clean
handset result is not a guarantee that other carriers will display the same label.
Every RevDesk customer's numbers automatically enroll in Number Reputation under a shared platform enterprise. No EIN required, no LOA to sign. Live spam-risk scores for every number from day one. The umbrella is reputation-only — it is never used for branded calling, so nothing but your own brand ever shows on a call.
Provide your legal name and EIN, sign an LOA in-app. Unlocks Branded Calling: your brand (legal name, logo, call reason) shows on every outbound call. Branded calling is exclusively own-brand — there is no shared-name fallback.
## How enrollment works
Every phone number you provision through RevDesk associates with Number Reputation automatically on purchase. The cron that refreshes cached spam scores runs daily and exposes them through `GET /v1/caller-trust/reputation/numbers/{phone}`. Freshly bought numbers need around 24 hours to accrue useful signal. You'll see `spam_risk: null` until Hiya has observed enough calls.
If you upgrade umbrella to own-brand, your numbers automatically disassociate from the umbrella and re-associate under your own enterprise. No outage in reputation tracking. Scores migrate with the number.
## Letter of Authorization
Registering an own-brand enterprise requires a signed LOA. RevDesk generates, signs, and stores it in-app:
1. Fill out the registration wizard (legal name, EIN, addresses, contacts).
2. The LOA text renders with your details. You review, check "I consent to conduct this transaction electronically," type your full legal name and job title, and click **Sign LOA**.
3. RevDesk generates a deterministic PDF, SHA-256 hashes it, cryptographically signs it with a server-held x509 certificate, and stores it in private storage. A copy emails to the signer.
4. The PDF uploads to the carrier's Documents API and references in the enterprise enable call.
The audit trail captured on every signed LOA includes IP address, user agent, session id, consent-checkbox timestamp, signing timestamp, document SHA-256 hash, and the certificate fingerprint used for the cryptographic seal. Compliant with the U.S. E-SIGN Act and UETA (typed signature, attribution, integrity, affirmative consent).
## Vetting lifecycle
```
DRAFT ──► PENDING_VETTING ──► APPROVED
│
└──► REJECTED ──► (resubmit → new PENDING_VETTING)
```
Hiya vets new enterprises in minutes in the typical case, sometimes longer. RevDesk polls status in the background. If the carrier has a webhook profile configured for your account, status updates happen in real time. Check current state at `GET /v1/caller-trust/enterprise` or subscribe to the `enterprise.status.updated` webhook event.
## When to use `fresh=true`
The `GET /v1/caller-trust/reputation/numbers/{phone}?fresh=true` parameter bypasses the cache and runs a live Hiya query. **It is billed per call.** RevDesk rate-limits this to once per 24 hours per number to prevent accidental bill shock. Dashboards should always read cached data. Only reach for `fresh=true` in remediation workflows where you've just made a change and need immediate confirmation.
## Branded Calling
Branded Calling lets your outbound calls arrive on recipient phones with your business name, logo, and a short call reason instead of a raw number. It helps recipients recognize a legitimate business call.
The underlying primitive is the **Display Identity Record (DIR)**, one per enterprise registration. Your DIR bundles the display name, logo asset, and call reason the carrier pushes to carrier-side display engines like Hiya.
### Current status
The Branded Calling endpoint is rolling out incrementally with our carrier. RevDesk has built the full customer-facing flow. You can save DIR drafts, upload logos, and mark them ready for submission today. A nightly worker (`cron/submit-pending-dirs`) watches for a readiness flag and batch-pushes all `PENDING_SUBMIT` DIRs once the endpoint goes live.
You can set up your brand today and it will go live automatically when Branded Calling launches.
### The DIR lifecycle
```
DRAFT ──► PENDING_SUBMIT ──► SUBMITTED ──► APPROVED
│
└─► REJECTED ──► (re-edit → DRAFT)
```
* **DRAFT.** You've saved some fields but haven't clicked Submit. Safe to edit freely.
* **PENDING\_SUBMIT.** You clicked Submit. RevDesk has queued the DIR for submission and awaits the carrier's Branded Calling endpoint going live.
* **SUBMITTED.** We've pushed the DIR to the carrier. Awaiting vetting.
* **APPROVED.** Live. Your brand renders on outbound calls.
* **REJECTED.** Carrier reviewer pushed back. See `rejection_reasons` on the DIR object. Editing any field moves status back to DRAFT so a resubmit is a clean slate.
### Required fields
| Field | Constraint |
| - | - |
| `display_name` | ≤ 64 chars. Usually the same as DBA. |
| `call_reason` | ≤ 120 chars. Short and specific. Example: "Annual roof inspection follow-up." |
| `logo` | PNG/JPEG/WebP, square, ≥ 200×200px. Upload via `POST /v1/caller-trust/brand/logo`. |
The matching enterprise registration must be `APPROVED` before a DIR is eligible for submission. DIRs inherit the enterprise's legal attestation.
### Typical integration flow
```bash theme={null} theme={null}
# 1. Save a draft
curl -X PUT https://api.revdesk.com/v1/caller-trust/brand \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"display_name":"Acme Roofing","call_reason":"Scheduled inspection follow-up"}'
# 2. Upload a logo
curl -X POST https://api.revdesk.com/v1/caller-trust/brand/logo \
-H "Authorization: Bearer $KEY" \
-F "file=@logo.png"
# 3. Submit when ready
curl -X POST https://api.revdesk.com/v1/caller-trust/brand/submit \
-H "Authorization: Bearer $KEY"
# 4. Poll status (or listen for webhooks)
curl https://api.revdesk.com/v1/caller-trust/brand \
-H "Authorization: Bearer $KEY"
```
### Relationship to Number Reputation
Both features ride on the same enterprise registration, but they're independent lifecycles. Your Number Reputation enrollment and DIR submission can be in different states. Common pattern: reputation `APPROVED` while DIR still `DRAFT`.
# Get every deal funded sooner
Source: https://docs.revdesk.com/concepts/deal-scanner
Deal Scanner is RevDesk's built-in CRM plus the paperwork check on every deal. Contacts, companies, and deals with a full activity timeline, a deal board, a payment solver, proposal links, and an AI review of the jacket so the packet goes to funding complete.
Deal Scanner is where a lead stops being a call that happened once and becomes a deal you can see, move, assign, close, and fund. It is a full CRM: **contacts** and **companies** in a directory, **deals** on a board or in a list, and a record for each with everything that ever happened on it. Every deal keeps the source that produced it, so the month's reporting adds up.
The name comes from the paperwork check described below: AI reviews every deal's jacket so nothing is missing when the customer leaves. Product page: [revdesk.com/deal-scanner](https://www.revdesk.com/product/deal-scanner).
Deal Scanner is available to dealership workspaces on RevDesk Business. It is an optional workspace surface, off by default. An admin turns it on under **Settings → General → Workspace surfaces**, where it is listed as Deal Operations. Until then, the page shows what it does rather than disappearing from the nav.
Turning it on also changes intake: from that point, inbound leads from calls, texts, and forms are captured as deals.
## Contacts and companies
**Contacts** is the directory: every person and company in one list, with tabs for **Lead sources** (what is arriving and from where) and **Source performance**. Filter to people or companies, sort by last activity, and open any row without losing your place — records open as a sheet over the list, so working through fifty rows does not mean fifty page loads.
A contact carries the details you would expect (title, email, phone, LinkedIn, notes), the deals they are on, and who owns them. A company rolls its people up: the contacts who work there, the open deals against it, the open pipeline total, and the next expected close.
## The timeline
Every contact, company, and deal record keeps a timeline: **notes, calls, texts, emails, meetings, and tasks**, newest first, filterable by type. Calls arrive with the recording and transcript. Nothing on a record is a claim without the conversation behind it.
### Gmail and Google Calendar
Connect Google once under **Settings → Integrations → CRM** and two sources feed the timeline, each of which you can leave unticked:
* **Gmail** — new email conversations with your customers land on their record.
* **Google Calendar** — upcoming meetings with customers land on their record.
Sync is forward-dated: it picks up new mail and meetings from the moment you connect, and never imports your history.
### The research agent
Records have an **Agent** tab where a research agent works a contact or company up for you: it searches public sources (LinkedIn, GitHub, X), shows every step and the source it came from as it runs, and suggests field values — a title, a profile link — that you accept rather than retype. What it found and where it found it stays on the record, so the enrichment is evidence, not a mystery.
The research agent is rolling out gradually and may not be enabled on your workspace yet.
## Deals
Deals live in two views of the same pipeline: a **board** (one column per stage, drag to move) and a **list** (a table you can sort by amount, close date, stage, or last activity, with the stage editable in place). Every deal carries an amount, an owner, a close date, and the customer — the company and the people on it, each with a role.
### The board
One column per stage, left to right, ending in the outcome.
| Stage | What it means |
| - | - |
| **New** | Untouched. Nobody has worked it yet. |
| **Working** | In conversation. |
| **Appointment set** | Booked to come in. |
| **Showed** | Came in for the appointment. |
| **Desking** | Working the numbers. |
| **Sold** | Closed and delivered. |
| **Lost** | Not moving forward. |
Filter by location, department, source, and owner, and sort by last activity, stalest first, or amount. Filters live in the URL, so a filtered board is a link you can send to someone.
Every pipeline also carries a **Disqualified** stage for leads that were never a real deal. It is kept out of pipeline value, so a pile of bad leads cannot inflate the month.
### Stage names follow your industry
The stages themselves are fixed, since automations and reporting key off them. What changes is the words on the board. Pick the template that matches how your team talks:
| Template | Reads as |
| - | - |
| **Dealership sales** | New, Working, Appointment set, Showed, Desking, Sold, Lost |
| **Dealership service** | New request, Contacted, RO scheduled, In shop, Estimate approved, Closed RO, Declined |
| **Generic CRM** | Demo booked, Qualified to buy, Decision maker bought in, Contract sent, Unqualified, Closed won, Closed lost |
| **Home services** | New request, Contacted, Estimate booked, Quoted, Ready to schedule, Job won, Lost |
| **Healthcare** | New inquiry, Outreach, Consult booked, Seen, Ready to book, Scheduled, Declined |
| **Legal** | New intake, Screening, Consult set, Consulted, Engagement sent, Retained, Declined |
A template can also hide a stage its vertical does not use: the Generic CRM template shows **Unqualified** on the board and skips the desking column, while the industry templates keep Disqualified off the board and out of the way.
Changing the template relabels the board. It never moves a deal.
## Desking a deal
Open a deal and it gets a **worksheet**: the deal's numbers, created the first time you pencil something. Each deal also carries a short **deal number** you can say out loud, so "pull up 2208" works.
On the worksheet you set the selling price, cash down, trade-ins, fees, and rebates. Then you build **scenarios**, one per payment option you want to show:
| Type | What it computes |
| - | - |
| **Cash** | Cash due, itemised tax, out-the-door. |
| **Finance** | Monthly payment, amount financed, finance charge, out-the-door. |
| **Lease** | Monthly payment, due at signing, depreciation and rent charge split. |
Name them the way you would say them, for example "72 @ 6.99". A worksheet can hold as many scenarios as you want to compare, and exactly one can be accepted.
### Trade-ins
A deal can carry more than one trade, because customers do. Each trade records the **allowance** (what the customer sees) alongside the **actual cash value** and the payoff. The gap between allowance and ACV is trade gross, and it stays on the manager's side of the screen.
### Working backwards from a payment
Customers usually arrive with a payment in mind rather than a price. The solver takes the payment they want and tells you what it would take to get there, by moving one of three levers:
Hold the price and term. What does the customer need to put down?
Hold the price and down payment. How many months gets there?
Hold the term and down payment. What price gets there?
The solver answers the question. It does not change the deal. You see what it would take and decide whether to pencil it.
### Tax and fees are per store
Tax rate, tax method, and the fee schedule belong to the rooftop, not the workspace, because a group with stores in two states does not have one answer. Retail and lease are taxed by different methods, and both are set per store.
Fees are a schedule you maintain rather than free text: each fee carries its own label, default amount, whether it is taxable, whether it capitalises into a lease, and whether it counts toward front gross. A doc fee usually does; title and registration usually do not. Editing the schedule tomorrow does not reprice a quote you saved today.
### The numbers do not drift
Everything is computed in exact integer cents. There is no floating-point rounding, because a payment on a signed document should be the payment you quoted.
Pencilling a deal also snapshots the unit, so an overnight inventory reprice never silently changes a customer's numbers.
## Sending the customer their numbers
When the options are ready, pick the ones the customer should see and send them. They get a text with a link.
Sending **locks** the scenarios you sent, so the numbers the customer is holding stop moving while you keep working the deal.
The link is good for **14 days**. You can revoke it from the deal at any point, and revoking kills the link immediately rather than at the next expiry. You can see whether it has been opened, and how many times.
Front gross, actual cash value, and unit cost are never included in what the customer receives, and are
stripped server-side rather than hidden in the browser.
## Deal Scanner: the paperwork
A deal funds when the jacket is complete, and a jacket with one thing missing waits until someone reaches the customer again. Deal Scanner keeps the paperwork on the deal and checks it before the customer leaves, so the packet goes to funding complete and the money arrives sooner.
**Requirements.** Under the store's desk settings, list what every deal must have on file: driver's license, proof of insurance, credit application, buyer's order, retail installment contract, title, odometer statement, trade payoff, registration, proof of income, or anything else you name. Each requirement can apply to purchases, trades, or both, to one location or all, and can name the stage a missing item blocks. Deals pick up the list the next time their jacket is opened.
**The jacket.** Open **Documents** from the desking header. Each document on the deal is a private, versioned file. Uploads are checked on arrival (PDF, JPEG, and PNG up to 25 MB; anything else is quarantined with the reason shown), and the kind is suggested from the file name and confirmed by a person, never assumed.
**Evidence and waivers.** A document offered for a requirement is reviewed and accepted or rejected, and the requirement moves to satisfied. A requirement that does not apply to this deal can be waived with a reason, which stays on the record. A signature requirement asks who signed and when before the document is offered; the signature is recorded as evidence rather than captured electronically.
**Packets.** When the jacket is ready, prepare a packet: one archive of the current documents, with any gaps listed, handed to accounting or funding. The packet records who has it and whether they accepted it. A returned packet comes back to the desk as work with what was missing, so the fix happens on the deal rather than by phone.
Requirements are configured by the store. RevDesk does not derive them from state or county rules, and
preparing a packet does not fund, title, or register anything. Those steps stay in your back-office systems.
## Reports
The monthly view breaks the funnel and win rate down by source, location, and team, with deal counts and pipeline value. Because every deal keeps its source from intake, the totals reconcile against where the leads actually came from.
### Revenue on Home
The Home dashboard opens on the pipeline itself: open pipeline by stage, closed-won against new pipeline by month, the largest open deals with their share of the pipeline, and the tasks that are overdue. Flip the scope between **just my deals** and the whole workspace. It uses your stage template's labels, so the chart reads the way your board does.
Live unit stock, so a deal is written against a real unit.
Who works the leads that land on the board.
# HIPAA & BAA coverage
Source: https://docs.revdesk.com/concepts/hipaa
How RevDesk supports HIPAA workloads, what our BAA covers, and how to request one.
## RevDesk's HIPAA posture
If your business handles Protected Health Information (PHI), you sign **one** Business Associate Agreement, and it is with RevDesk. We've already executed BAAs with the subprocessors in our stack that require them. You don't need to chase individual provider BAAs; we handle that upstream.
Once your BAA is on file, we set `hipaa_enabled` on your workspace. That flag activates real runtime controls (described below), not just a contractual posture.
## Our subprocessor BAA status
We use several third-party services to deliver RevDesk. Here's where each one stands today. This is informational only; you don't act on this list.
| Service | Role | RevDesk's BAA status |
| - | - | - |
| Tier-1 carrier (voice, SMS, SIP trunking) | Carrier | **Conduit Exception**: the carrier transmits PHI but doesn't access message content, so no BAA is required. Encryption in transit and at rest, access controls, and audit logging still apply. |
| LiveKit (real-time audio) | Media transport + AI agent runtime | **Executed.** |
| Vercel (hosting) + Vercel Blob (recording storage) | Platform | **Executed** under Enterprise BAA. |
| Managed Postgres | Data store | **Executed** with our managed Postgres provider. |
| OpenAI (LLM) | AI inference — the voice model on every call, and every text feature | **BAA-eligible enterprise tier**: we route HIPAA workspaces to BAA-covered model paths and block fallback to non-BAA providers. |
| Deepgram (STT) | Transcription | **Executed.** |
| Stripe | Billing | **Executed.** We avoid putting PHI in invoice descriptions or metadata regardless. |
If you need documentation of any individual subprocessor BAA for your own compliance audit, email `compliance@revdesk.com` and we'll provide it.
## How to request a BAA from RevDesk
**Self-serve is the fastest path.** On RevDesk Business or Custom, an owner or admin can turn HIPAA mode on in **Settings → Organization → HIPAA**, review the standard BAA as a PDF, and accept it in the dialog. That acceptance is a complete two-party execution — our side is pre-signed — and the runtime controls below take effect immediately.
If you need the agreement marked up, or you are not on a qualifying plan:
1. **Email `compliance@revdesk.com`** with subject "HIPAA BAA Request".
2. **Include:**
* Your legal entity name
* The workspace or team that will handle PHI
* Your primary compliance contact (name + email)
3. **What happens next:**
* We respond within 2 business days with our standard BAA.
* For customers who can sign as-is: same-day workspace activation once the BAA is countersigned.
* For customers needing legal markup: we route through our counsel; typical close in 5–10 business days.
4. **After signing:** We record execution on your workspace (`baaSignedAt`) and flip `hipaa_enabled = true`.
If you're a partner with consistent healthcare volume, ask us about platform-wide HIPAA, a single agreement that covers your entire downstream customer base. Details on the [HIPAA add-on page](/integrations/other/hipaa).
## What `hipaa_enabled` does
A workspace with `hipaa_enabled: true` enforces the following at runtime:
1. **Voice pinned to BAA-covered providers**: voices from providers without a BAA become unselectable, and an assistant still holding one is switched to a covered default at config-build time.
2. **Call content stripped from external egress**: recordings and transcripts are removed from outbound webhook payloads and the public API for a HIPAA workspace, including legacy rows that still hold content.
3. **Recording retention defaulted to 30 days**: enabling HIPAA sets `recordingRetentionDays` to **30** for every number currently retaining longer (the minimum-necessary default). You can adjust it per number afterward — there is no statutory cap on retaining PHI you are the custodian of — or turn call recording off entirely for the workspace in **Settings → Privacy** (`callRecordingDisabled`).
4. **Audit log**: compliance-relevant mutations (flag flips, BAA recording, retention changes) are recorded.
What it does **not** do, and you should plan around:
* **It is not a storage switch.** HIPAA permits BAA-covered storage, so recordings and transcripts keep being stored unless you turn them off yourself in **Settings → Privacy**.
* **It does not narrow the sub-processors outside the call path.** Product analytics, error monitoring, and contact enrichment are not BAA-covered and are not disabled by the flag. We send them no call audio, transcripts, or message content — but do not run contact enrichment against a record that is PHI.
* **It does not block a greeting that omits the recording disclosure.** Our default greetings carry it in 40+ languages, and the Compliance Center flags any drift between a greeting and the recording policy, but a custom greeting still saves.
Flipping the flag is reversible, and we don't mass-delete historical data. Enabling it yourself requires accepting the click-through BAA if none is on file; when our team enables it for you, that memorializes a BAA executed offline.
## Security baseline RevDesk always provides
Regardless of whether `hipaa_enabled` is set:
* TLS 1.2 or higher in transit, SRTP on real-time media, AES-256 at rest.
* Encrypted credential storage (`REVDESK_ENCRYPTION_KEY`).
* Row-level access control: every API query runs through `buildOwnershipFilter` which scopes results to the authenticated principal's org/team visibility.
* Audit log on every mutation via tRPC middleware.
* Breach notification procedures per § 164.410.
Missing something your compliance officer needs? Email `compliance@revdesk.com`.
# Your stock, where your team already works
Source: https://docs.revdesk.com/concepts/inventory
Live unit inventory synced from your DMS feed, so anyone answering a call can say what is actually on the lot and deals get written against a real unit.
Inventory is your live unit stock, synced in from your DMS or inventory feed on a schedule. It answers the question that comes up on almost every inbound call: do you actually have it?
Inventory is an optional workspace surface, off by default. An admin turns it on under **Settings → General
→ Workspace surfaces**. Until then, the page explains what it does rather than disappearing from the nav.
## What it gives you
* **Your team can answer "is it in stock?" on the call**, instead of promising to check and calling back.
* **Your agents can answer it too.** A voice agent looks the unit up mid-call rather than taking a message.
* **Deals get written against a real unit.** A deal on [Deal Scanner](/concepts/deal-scanner) can point at an actual VIN, so nobody works numbers on something that sold yesterday.
## The catalogue
One row per unit, with the header showing the total count and how long ago the feed last synced.
Search by VIN, make, model, or stock number, and filter by:
| Filter | Values |
| - | - |
| **Location** | Any rooftop in the feed |
| **Body style** | Whatever your feed carries, for example SUV, sedan, truck |
| **Condition** | New, used, or certified pre-owned |
Each unit carries the fields you would expect to quote from: year, make, model, trim, price, MSRP, mileage, exterior colour, body style, location, and photos. Recreational-vehicle feeds additionally carry class, sleeping capacity, length, and slide-outs.
## How the feed works
You connect a feed once and it keeps itself current. Two shapes are supported:
| Transport | How it runs |
| - | - |
| **Pull** | RevDesk fetches your feed on a schedule. |
| **Push** | Your DMS posts to a RevDesk endpoint whenever stock changes, authenticated with a token issued for that feed. |
Feeds rarely agree on column names, so each one carries a mapping from your feed's columns onto the fields RevDesk understands. Common column names are recognised automatically, and anything unusual is mapped once at setup.
Units are keyed on VIN, so the same unit arriving in two syncs updates in place rather than duplicating. A unit that stops appearing in the feed is marked sold or removed rather than silently vanishing, so a deal already written against it still resolves.
Sync failures surface on the feed rather than failing quietly, along with the count of units in the last successful run.
Work every lead as a deal, against a real unit.
Who answers the call that asks what is on the lot.
# Teach your agents your business
Source: https://docs.revdesk.com/concepts/knowledge-base
A knowledge base is what makes the AI useful. Teach it your business once and every call benefits from it.
The knowledge base is everything your AI needs to handle a call well: what you do, what you charge, what your policies are, and what to say when a caller asks the question you hear every week.
Think of it as onboarding a new employee, with the difference that everything you write is available on every call from that moment forward.
## What goes in
* **Business basics.** Name, location, hours, service area.
* **Services.** What you offer, with pricing and what is included.
* **Policies.** Cancellation, payment, guarantees, emergency rates.
* **FAQs.** The questions you hear every week, in the words your customers actually use.
* **Procedures.** How to handle the unusual cases like emergency calls, after-hours requests, or escalations.
## Why it is worth the 30 minutes
The same caller, asking the same question, can lead to two very different outcomes depending on whether the AI has the answer.
Without a knowledge base:
> **Caller:** Do you offer free estimates?
> **RevDesk:** I am not sure about that. Let me take your information and have someone call you back.
With a knowledge base:
> **Caller:** Do you offer free estimates?
> **RevDesk:** Yes, all of our estimates are free. Want to grab a slot for tomorrow?
One conversation produces a callback queue. The other produces a booked appointment. Same caller, same 30 seconds.
***
## Start with the essentials
* Business name and tagline
* Address and service area
* Business hours (including holidays)
* Phone number and email
* Website URL
### Add your services
For each service, include:
| Field | Example |
| - | - |
| Service name | AC Repair |
| Description | We fix all AC issues, including no cooling, strange noises, and leaks |
| Price range | \$89-\$250 depending on the issue |
| Duration | Usually 1-2 hours |
| What's included | Diagnosis, parts, labor, 90-day warranty |
### Add policies
Common policies to document:
* **Cancellation.** 24-hour notice required
* **Payment.** Cash, card, financing available
* **Guarantee.** 100% satisfaction or we fix it free
* **Service area.** Within 30 miles of downtown
* **Emergency rates.** After-hours calls have additional fee
### Add FAQs
Document the questions you get asked most:
| Question | Answer |
| - | - |
| Do you offer financing? | Yes, we offer 0% financing for 12 months on jobs over \$1,000 |
| Are you licensed and insured? | Yes, fully licensed (#123456) and insured |
| How quickly can you come out? | Usually within 24-48 hours, same-day for emergencies |
| Do you work on all brands? | Yes, we service all major brands |
***
## How to add knowledge
### Option 1: Type it in
The simplest approach. Go to your knowledge base and add entries:
```
Q: Do you offer free estimates?
A: Yes, all our estimates are completely free with no obligation.
```
### Option 2: Import from documents
Upload existing documents and RevDesk extracts the knowledge:
* PDF brochures
* Website content
* Training manuals
* FAQ documents
### Option 3: Import from your website
Give RevDesk your website URL and it will:
1. Crawl your pages
2. Extract relevant information
3. Organize it into your knowledge base
You review and approve what gets added.
***
## Organizing knowledge
### Categories
Group related information:
* **Services.** What you offer
* **Pricing.** Costs and packages
* **Policies.** Rules and procedures
* **Company.** About your business
* **FAQ.** Common questions
### Priority levels
Mark which information is most important:
* **High priority.** Always mention (special offers, key differentiators)
* **Normal priority.** Share when relevant
* **Low priority.** Only if specifically asked
***
## Testing your knowledge
After adding information, test it:
1. **Call your number** and ask questions
2. **Review transcripts** to see how AI responded
3. **Identify gaps** where AI didn't have the answer
4. **Add missing information** to your knowledge base
Repeat this cycle for the first few weeks. Each addition closes a gap for the next call.
***
## Best practices
* Use natural language - Include variations of questions - Update regularly - Add context and nuance -
Test frequently
* Use jargon without explanation - Leave outdated information - Assume AI will guess - Add irrelevant
details - Forget edge cases
### Write like you talk
Bad:
> "Service initiation requires a minimum 48-hour advance notice period."
Good:
> "We need at least 2 days notice to schedule a service. For emergencies, call our emergency line."
### Include the why
Bad:
> "We require a \$50 deposit."
Good:
> "We require a \$50 deposit to hold your appointment. This goes toward your final bill. If you need to cancel, just give us 24 hours notice for a full refund."
### Anticipate follow-ups
If the AI says "we offer free estimates," callers will ask:
* "How do I schedule one?"
* "How long does an estimate take?"
* "What if I don't go with you?"
Add those answers to the knowledge base so the agent can answer the follow-up without bouncing the caller into a callback queue.
### Review monthly
Make a recurring 30-minute review part of your operating rhythm:
* Add any new services
* Update pricing
* Remove discontinued offerings
* Add new FAQs from real call transcripts
***
## When AI doesn't know
Even with a great knowledge base, there will be questions AI can't answer.
When this happens, AI:
1. Acknowledges it doesn't have that information
2. Offers to take the caller's info for follow-up
3. Notifies you about the knowledge gap
You'll see these gaps in your dashboard and can add the missing information.
***
## Knowledge base vs. prompts
| Knowledge Base | Custom Prompts |
| - | - |
| Facts about your business | How to handle situations |
| "We're open 9-5" | "If someone seems angry, be extra empathetic" |
| "We offer 3 services" | "Always mention our satisfaction guarantee" |
Use knowledge base for **what**. Use prompts for **how**.
***
Start building your knowledge base
Learn about custom prompts
# Number Registration
Source: https://docs.revdesk.com/concepts/number-registration
How RevDesk registers eligible hosted US phone numbers with the analytics engines behind carrier spam labels.
## What it is
The **Free Caller Registry** is a joint intake used by the analytics engines that score caller
reputation for major US carriers: **Hiya**, **TNS**, and **First Orion**. Registering a number
associates it with the business identity that uses it and helps the engines recognize legitimate
calling.
RevDesk manages this workflow for eligible US numbers hosted on RevDesk. It is an included part of
operating those numbers, not a separate product or a bring-your-own-number registration service.
Registration is preventive. It establishes the business identity behind a number, but it does not guarantee
that carriers will never label a call. Calling behavior, complaint rates, consent, and list quality still
affect reputation. For a number already labeled, see [caller trust](/concepts/caller-trust).
## How it works
1. Add or port an eligible US phone number to RevDesk.
2. Complete the workspace's business identity and caller-trust profile.
3. Confirm the authorization attestation in **Numbers → Trust**.
4. RevDesk submits the hosted number and tracks acknowledgments from the registry engines.
The workflow is asynchronous. You can review its status in **Numbers → Trust** or through the
read-only API endpoints.
## Lifecycle
| Status | Meaning |
| - | - |
| `pending` | RevDesk queued the hosted number for submission |
| `submitted` | The registry accepted the submission; acknowledgments are pending |
| `active` | At least one engine acknowledged receipt |
| `failed` | The submission needs another attempt or operations review |
| `cancelled` | RevDesk stopped monitoring this registration |
Use `GET /v1/number-registrations` to list registrations and
`GET /v1/number-registrations/{id}` to inspect one registration's latest submission and per-engine
acknowledgments. New registrations are created from the authenticated RevDesk number workflow, not
through the public API.
## Webhooks
Subscribe via [`POST /v1/webhook_subscriptions`](/api-reference/webhooks):
* `registration_submitted`: the registry accepted a submission
* `registration_confirmed`: the first engine acknowledgment arrived
* `registration_failed`: a submission failed after retries
Registration for eligible RevDesk-hosted numbers is included. There is no separate Number
Registration subscription or per-number registration line item.
# Outreach audit & consent records
Source: https://docs.revdesk.com/concepts/outreach-audit
How RevDesk captures consent metadata, attaches disclosures, and persists an audit trail for outbound communications.
RevDesk records what disclosures were attached to every outbound call and SMS, plus the per-channel opt-in state for every contact, so you can produce an audit trail on demand.
***
## What RevDesk handles for you
Several compliance steps are run automatically on every outbound action. You don't need to configure or maintain them:
| Item | What RevDesk does | Where it shows up |
| - | - | - |
| **National DNC scrubbing** | Every outbound contact is checked against the federal Do-Not-Call registry plus your workspace opt-out list before a call or SMS leaves the platform. Hits are blocked and logged. | Compliance Center → Active disclosures → "DNC scrubbing" |
| **TCPA quiet-hours enforcement** | Calls outside 8am–9pm in the recipient's local time are deferred. State-specific rules (FL/MD/MA/OK/WA/WY 8am–8pm, AL/LA/MS/UT/SD/RI Sunday ban) are layered on top. | Compliance Center → Active disclosures → "Calling hours" |
| **Recording disclosure** | Default outbound greeting includes "this call is being recorded" in the recipient's language (50+ localized). Two-party-consent state recipients get an extra compose-time warning if your greeting is missing the phrase. | Compliance Center → Active disclosures → "Recording disclosure" |
| **SMS STOP append** | First outbound SMS to each contact auto-appends "Reply STOP to opt out." Inbound STOP/UNSUBSCRIBE/END/CANCEL/QUIT keywords are honored automatically. A phone opt-out stops calls, SMS, and iMessage to that number; email and WhatsApp opt-outs apply to that channel. | Compliance Center → Active disclosures → "SMS STOP instructions" |
| **Disclosure attestation log** | Every outbound call and SMS persists `disclosureAttested` / `stopInstructionsAttested` plus a two-party-state flag at send time. | Compliance Center → Last 30 days + "Export audit" |
If you see a **"We'll do it"** badge anywhere in the Compliance Center, that means the underlying check is one of the items above. It is already configured, always-on, no action required.
***
## Where to find it
Open the **Compliance Center** widget on the Outreach page. It surfaces:
* The workspace's TCPA/DNC acknowledgement state.
* Active disclosures (recording, SMS STOP, TCPA quiet hours, DNC scrubbing).
* Per-active-campaign compliance checkmarks.
* Consent record counters and recent events.
* 30-day disclosure attestation rates.
* "Export full audit" CSV.
***
## Consent record model
For each contact, RevDesk maintains per-channel state:
```
voiceOptIn / voiceOptedInAt / voiceConsentSource / voiceConsentSender / voiceConsentUseCase
smsOptedIn / smsOptedInAt / smsConsentSource / smsConsentSender / smsConsentUseCase
emailOptIn / emailOptedInAt / emailConsentSource / emailConsentSender / emailConsentUseCase
firstInboundCallAt / firstInboundSmsAt / firstInboundEmailAt
```
### Sources of consent
| Source | When it's written |
| - | - |
| `manual_admin` | Admin captured consent via the Capture Consent dialog on the contact-detail page. |
| `csv_upload` | Consent metadata supplied in an inbound CSV import. |
| `crm_sync` | Consent provided by an upstream CRM sync. |
| `inbound_initiated` | Set automatically when a contact initiates an inbound call or SMS to your workspace. |
| `web_form`, `verbal_call`, `partner_api`, etc. | Free-form values you supply. |
### Capture via API
```ts theme={null}
trpc.viewer.contacts.captureConsent.mutate({
contactId: "...",
channel: "voice",
optedIn: true,
source: "verbal_call",
sender: "user:42",
useCase: "sales_followup",
});
```
***
## Disclosure attestation
Every outbound `PhoneCall` row records:
* `disclosureAttested`: true when the live greeting includes a recording-disclosure phrase (English variants and 50+ localizations).
* `disclosureText`: the disclosure phrase actually attached.
* `twoPartyStateAtCallTime`: true when the recipient's area code maps to an all-party-consent state (CA, CT, DE, FL, IL, MA, MD, MI, MT, NV, NH, OR, PA, WA).
Every outbound `Message` row records:
* `stopInstructionsAttested`: true when the body contains STOP / opt-out instructions (auto-appended by `maybeAppendOptOut` on the first message per contact, or already present in the body).
These columns are written at send time rather than derived from later log scans, so the audit reflects exactly what the recipient saw or heard.
***
## Audit export
The Compliance Center widget's "Export full audit" button calls `viewer.compliance.exportOutreachAudit` and returns a CSV containing:
```
occurredAt, channel, direction, contactId, contactName, contactPhone, toNumber, fromNumber,
senderUserId, disclosureAttested, disclosureText, stopInstructionsAttested,
twoPartyStateAtCallTime, voiceOptIn, voiceConsentSource, voiceConsentSender, voiceConsentUseCase,
smsOptedIn, smsConsentSource, smsConsentSender, smsConsentUseCase,
firstInboundCallAt, firstInboundSmsAt
```
Default window is the last 30 days; the API accepts `since` / `until` parameters for arbitrary ranges. Records are scoped to your accessible teams only.
***
## Related
* [Compliance & Regulations](/getting-started/features/compliance)
# Referral Network
Source: https://docs.revdesk.com/concepts/referral-network
Route the matters and patients you can't take to a partner you choose, and stop losing the value of the demand you already generate.
Every practice turns work away: the conflicted-out matter, the full panel, the case outside your specialty. Today that caller hangs up and dials your competitor. The Referral Network turns the same call into routed, documented value. You decide which work routes and who receives it; RevDesk runs the intake, the handoff, and the paper trail.
## How it works
1. **Intake as usual.** RevDesk answers, qualifies, and screens every caller, including the ones you can't serve.
2. **Route by your rules.** Declined work goes to a partner you chose in advance. RevDesk never recommends, ranks, or matches. Routing is your standing decision, not an algorithm's.
3. **Documented end to end.** The handoff, the consent, and the outcome are recorded and reported back, so nothing disappears into a voicemail.
## For law firms
Declined matters become revenue instead of dead ends, inside the lines your bar draws:
* Matters you conflict out of, or that fall outside your practice areas, route to a firm you selected.
* Fee division happens lawyer-to-lawyer under your state's Rule 1.5(e), with client consent confirmed in writing.
* RevDesk charges flat platform fees, never a percentage of legal fees, never contingent on outcome.
* RevDesk is not a lawyer referral service: it doesn't recommend or rank lawyers, and it doesn't match callers by analyzing their legal problem.
How RevDesk handles intake and follow-up, on the business page.
## For healthcare practices
Patients you can't take stop being dead air on the line:
* Full panel, out-of-network, or outside your specialty, the patient routes to a practice you trust instead of cold-calling the next result on Google.
* Inbound referrals are captured and outbound referrals are worked end to end, so the loop closes with the referring provider automatically.
* Providers and staff make every referral call through the practice's number, so personal cells stay private.
Post-op check-ins, recall, prescriber relays, and referrals on the healthcare solutions page.
## Join
The network is in early access and free to join. Tell us what you route and who you trust:
[sales@revdesk.com](mailto:sales@revdesk.com) — early access, free to join.
# Sub-entities
Source: https://docs.revdesk.com/concepts/sub-entities
Provision and manage multi-tenant organizations (clinic locations, franchisees, end-client accounts) under one umbrella organization.
## When you need sub-entities
Your org has multiple semi-independent business units and you want each to have its own phone numbers, agents, and settings, all managed from one RevDesk seat. Common cases:
* **Healthcare network.** One parent entity, many clinic locations, each with its own outbound numbers.
* **Franchise.** One brand, many franchisees.
* **Agency.** One agency, many end-client workspaces.
Every sub-entity is a `Team` with `parent_organization_id` pointing to your umbrella org. Members can belong to the umbrella, a specific sub-entity, or both. Phone numbers, calls, reputation tracking, and brand registration are all sub-entity-scoped.
## What sub-entities share vs. own
| Resource | Shared with umbrella | Owned by sub-entity |
| - | - | - |
| Caller Trust enterprise (LOA, EIN, DIR) | Yes. One umbrella LOA covers every sub-entity by default | Optional. A sub-entity can register its own enterprise if it has a separate legal identity |
| Phone numbers | ✗ | ✓ |
| Agents + voice config | ✗ | ✓ |
| Call history + recordings | ✗ | ✓ |
| Billing | Pooled at the umbrella org | n/a |
| HIPAA posture | Inherited on creation, editable per sub-entity | n/a |
The reputation-enrollment service walks the hierarchy automatically: when a phone on a sub-entity is provisioned, it looks for a team-level enterprise first, then the parent org's enterprise, then falls back to the Cell Labs umbrella. **So you register once at the umbrella, and every sub-entity's numbers are covered.**
## API key scopes
Two scopes matter for sub-entities:
* **Org-scoped key**: `auth.teamId` = umbrella org id. Sees every sub-entity under the umbrella. Use for HQ engineering or internal tools.
* **Sub-entity-scoped key**: `auth.teamId` = sub-entity id. Sees **only that sub-entity's** resources (phone numbers, calls, contacts). Use when issuing keys to per-location ops or to a vendor managing a single location.
A sub-entity-scoped key reading caller-trust endpoints (enterprise / brand / reputation) sees the **parent umbrella's** registration. Your one Branded Calling registration covers every child automatically.
To issue sub-entity-scoped keys: as the umbrella admin, create them via the Settings → API Keys UI with `teamId = subEntityId`, or have a RevDesk admin issue one on your behalf from the carrier-registrations admin panel.
## Endpoints
All sub-entity endpoints require:
* An umbrella-org-scoped API key, **OR**
* A user-scoped API key where the user is `ADMIN` / `OWNER` of exactly one organization.
Required scopes: `sub_entities:read`, `sub_entities:write`.
### Create a sub-entity
```bash theme={null} theme={null}
curl -X POST https://api.revdesk.com/v1/sub-entities \
-H "Authorization: Bearer $UMBRELLA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Northside Clinic",
"slug": "northside-clinic"
}'
```
```json theme={null} theme={null}
{
"data": {
"id": 142,
"name": "Northside Clinic",
"slug": "northside-clinic",
"parent_organization_id": 42,
"hipaa_enabled": true,
"member_count": 1,
"phone_number_count": 0,
"created_at": "2026-05-20T18:00:00.000Z"
}
}
```
The new sub-entity inherits the parent's HIPAA posture by default. The umbrella user who issued the request is auto-added as `OWNER`.
### List sub-entities
```bash theme={null} theme={null}
curl https://api.revdesk.com/v1/sub-entities?limit=50 \
-H "Authorization: Bearer $UMBRELLA_KEY"
```
Paginated. Pass `cursor=` from `meta.cursor` to fetch the next page.
### Get one
```bash theme={null} theme={null}
curl https://api.revdesk.com/v1/sub-entities/142 \
-H "Authorization: Bearer $UMBRELLA_KEY"
```
### Update
```bash theme={null} theme={null}
curl -X PATCH https://api.revdesk.com/v1/sub-entities/142 \
-H "Authorization: Bearer $UMBRELLA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Northside Clinic - Pediatrics",
"hipaa_enabled": true
}'
```
Changing `hipaa_enabled` requires the **`compliance:write`** scope in addition to `sub_entities:write` — it is a sensitive control and is never granted to a key by default. Setting `hipaa_enabled: true` also requires the umbrella org to have an executed BAA on file. If the flag won't flip, email `support@revdesk.com`.
## Worked example: onboard a new location end-to-end
```bash theme={null} theme={null}
# 1. As the umbrella, create the sub-entity.
NORTHSIDE=$(curl -s -X POST https://api.revdesk.com/v1/sub-entities \
-H "Authorization: Bearer $UMBRELLA_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Northside Clinic", "slug": "northside"}' | jq -r '.data.id')
# 2. Provision a number for that sub-entity.
# (Until team_id is exposed on POST /v1/phone-numbers, use the
# dashboard's sub-entity switcher and then read it back via the API.)
curl https://api.revdesk.com/v1/phone-numbers?team_id=$NORTHSIDE \
-H "Authorization: Bearer $UMBRELLA_KEY"
# 3. (Optional) Issue a sub-entity-scoped API key for the clinic's ops team
# or vendor. They will see ONLY Northside's numbers, calls, and contacts.
# Do this through the Settings → API keys UI with teamId=$NORTHSIDE
# or via the admin carrier-registrations panel.
# 4. With the sub-entity-scoped key, the clinic can read the umbrella's
# brand registration (inherited automatically, no separate Branded
# Calling submission needed):
curl https://api.revdesk.com/v1/caller-trust/brand \
-H "Authorization: Bearer $NORTHSIDE_KEY"
# => returns the umbrella's brand DIR
```
## Tenant isolation guarantees
A sub-entity-scoped key:
* **Reads**: filters every resource (phone numbers, calls, contacts, messages, reputation) to `teamId = subEntityId` only. Never sees the umbrella's or sibling sub-entities' records.
* **Writes**: created resources are tagged with `teamId = subEntityId` so they're visible only within that sub-entity.
* **Caller-trust reads** (enterprise, brand, reputation): walk up to the parent umbrella's registration. Read-only: the sub-entity can see the brand it's calling under, but cannot modify it. To edit, use an umbrella-scoped key.
If a sub-entity needs its own brand (separate legal entity, distinct CNAM), register it via `POST /v1/caller-trust/enterprise` using a key scoped to that sub-entity, then sign + submit via `POST /v1/caller-trust/enterprise/sign-and-submit`.
## HIPAA inheritance
Creating a sub-entity under a HIPAA-enabled umbrella **inherits the flag at creation** — the child starts HIPAA-enabled, fail-closed. This is **non-downgradeable**: while the umbrella is HIPAA-enabled, an attempt to set `hipaa_enabled: false` on a child is **rejected** (`403`). A child can only be HIPAA-disabled if the umbrella itself is not HIPAA-enabled. Enabling HIPAA on a child also defaults its call-recording retention to 30 days (adjustable per number afterward).
The parent's BAA covers all sub-entities under it, with no separate BAA per child. The `hipaa_enabled` flag on each sub-entity controls whether the runtime HIPAA gates apply to that specific child.
See [HIPAA & BAA coverage](/concepts/hipaa) for what this flag gates and how to request a BAA.
# Give an agent a Task
Source: https://docs.revdesk.com/concepts/tasks
A Task is work assigned to an AI Agent. Learn how Tasks decide what happens next, where they stop, and what the record shows.
A **Task** is an outcome you want, handed to the [agents](/concepts/agents) who will chase it: book the test drive, get the vehicle back in for service, complete the next application step, win the customer back.
You give it four things.
| | |
| - | - |
| **Objective** | Plain language. "Get lapsed service customers back in before the end of the quarter." |
| **Who it is for** | A list of people, or an event that produces them one at a time. |
| **Guardrails** | Which channels, quiet hours, how many touchpoints, what consent you hold. |
| **Success** | Which outcome ends it. Booked, sold, enrolled, signed. |
From there it decides what to do next after every reply, on whichever channel is most likely to get an answer, until the objective is met or a guardrail stops it.
## Two kinds
Every Task is one of two scopes, and the board labels each row so you can tell how people enter it.
Works a list of people and finishes. "Win back the 412 customers who have not been in for 14 months."
Stays on and opens one pursuit each time its event fires. "Whenever a form is submitted, reach that lead."
These are not two systems. An audience task enrols many people at once; a triggered task produces one at a time. After that first moment they are identical, which is why they share one board, one runtime, and one record.
## The loop
Everything a Task does is one loop, run per person until it wins or a guardrail stops it. The names below are exactly the ones on the timeline, so what is described here is what gets recorded.
```
AN AUDIENCE TASK A TRIGGERED TASK
enrols a list of people fires on an event, each time
│ │
└──────────────┬───────────────┘
▼
┌─────────┐
┌───▶│ SIGNAL │ something happened
│ └────┬────┘
│ ▼
│ ┌───────────┐
│ │ REASONING │ what should happen next?
│ └─────┬─────┘
│ ▼
│ ┌────────┐
│ │ TASK │ call her Tuesday morning
│ └───┬────┘
│ ▼
│ ┌─────────────┐
└──│ INTERACTION │ the call happens, and what
└─────────────┘ comes back is the next signal
exits on OUTCOME, or when a guardrail stops it
```
Two more bands appear on a real timeline. **MEMORY** is what the agent retained rather than what it did, and **SYSTEM** records the machinery moving, such as a scheduled call being cancelled because the person replied first.
## Not a drip sequence
A drip sends step three on day four whether or not anyone replied. A Task re-decides every time something happens.
* **It reasons between touchpoints.** She replies to texts and never picks up, so the next touchpoint is a text. She has gone quiet through six touchpoints, so it stops rather than becoming the reason she opts out.
* **It remembers across conversations.** What a customer said in March is context for the call in July. Memory belongs to the person and outlives the Task that learned it.
* **It ends in an outcome**, not in a step count.
## Watching one run
Open a Task and you get the people in it. Open a person and you get their thread, band by band, in the order it happened.
Here is a real sequence, shortened.
| Band | |
| - | - |
| **Signal** | Enrolled from the lapsed-service list. No visit in 14 months. |
| **Reasoning** | Text first. This list replies to SMS far more often than it picks up. |
| **Interaction** | "Hi Dana, it's Nora at Camperland. Your last service was a while back — want me to find you a slot?" |
| **Signal** | Dana replied: "what would it cost?" |
| **Memory** | Dana asks about price before she will book. Lead with the number. |
| **Reasoning** | She replied, so the scheduled day-2 call is now the wrong move. Answer her instead. |
| **System** | Day-2 call cancelled for this person. The rest of the Task is unaffected. |
| **Interaction** | Sent the oil-change price and offered Thursday 2:00pm or Friday 10:30am. |
| **Outcome** | Visit booked, Thursday 2:00pm. |
That day-2 line is the whole idea in one row. You wrote a plan, she did something that made the plan wrong, and it departed from the plan and said why.
## Guardrails are a ceiling, not a suggestion
Guardrails are copied onto every person the moment their pursuit opens, and the planner is structurally unable to exceed them.
| Guardrail | What it does |
| - | - |
| **Channels** | Which of call, text and email it may use. |
| **Quiet hours** | Local to the person, not to your office. |
| **Most touchpoints** | A hard ceiling per person, counted across every channel. |
| **Consent** | Where permission came from, recorded on the Task. |
Copying rather than referencing matters: editing a Task tomorrow cannot retroactively loosen what was already agreed for someone being worked today.
Whether to make contact, on which channel, and when are decided by a policy layer, not by a language model, and every decision is written down with its reason. The model words the message once the policy layer has already decided to send one.
This is why "why did you contact my customer at 9pm" is answerable from a rule rather than from a sampled token. In lending and healthcare, buyers audit exactly this.
## Outcomes, and disputing one
A Task ends in an outcome, and every outcome carries the interactions that produced it, so it can be opened to see the calls and messages behind it.
If one is wrong, dispute it from the Task and say why. The charge is held the moment you do. Disputing is limited to owners and admins, because it moves money.
## Long horizon is not long running
A 45-day loan origination is long-horizon because it spans dozens of decision points, not because a process runs for 45 days. A Task is a durable record; the planner that advances it is a short-lived background operation woken by a signal or a timer.
Cost scales with the number of decisions, not with elapsed time.
## What improves between runs, and what does not
Several different things get called "learning". Only some of them happen here.
| Behaviour | Status |
| - | - |
| **Remembering a customer** | Yes. Memory belongs to the person and outlives the Task. It is shown on the timeline as a MEMORY band and loaded into the next conversation. |
| **Retrieving business information** | Yes. The knowledge base, connected inventory, and FAQs are searched during a conversation so the answer is the business's answer, not a guess. |
| **Choosing the next action from live signals** | Yes. The policy layer picks channel and timing from what this person has answered on, stated preferences, quiet hours, consent, and the touchpoint ceiling. It is a rule set, not a learned model, and every decision is recorded with its reason. |
| **Optimizing timing, wording, or channel from outcomes** | Not yet. Nothing changes the rules or the prompts on its own. Managers read transcripts, summaries, and flagged gaps and update knowledge, rules, and prompts themselves. Automatic optimization is an ambition, not a shipped capability. |
| **Evaluating a change against results** | Only for website tests in Agentic A/B Testing, which compares a variant to control with a sequential test and is not yet generally available. There is no equivalent loop for conversation behaviour. |
| **Training or fine-tuning a model on your data** | Only if you opt in. "Help improve RevDesk" is off by default. When an admin turns it on, redacted conversations and their outcomes can train RevDesk's global model. Your data never trains anyone else's models, one customer's data is never shared with another, and SMS threads, HIPAA workspaces, and contacts who opted out are excluded. |
Because the record is shared, "the agent did something different this week" always traces to a rule you
changed, knowledge you added, or a signal the customer sent, never to an unobserved update.
## Getting a good one
* **Say the outcome, not the activity.** "Book a service visit" beats "call these people." The objective is what tells it when to stop.
* **Set the touchpoint ceiling deliberately.** It is the single control that decides whether a Task feels persistent or feels like harassment.
* **Start with one list.** Watch a few threads end to end before pointing it at thousands.
* **Read the reasoning bands.** They are where you find what it believes about your customers, and where a wrong assumption shows up early.
## What's next
Who works your Tasks, and how you hire them.
Where a lead becomes a deal you can move and close.
# Real-time calendar booking
Source: https://docs.revdesk.com/concepts/your-calendar
Your AI books against your live calendar. Real availability, real bookings, no manual coordination after the call.
Calendar access turns the AI from a message-taker into a booking agent. RevDesk reads your real availability and writes appointments directly, so every call can end with a confirmed slot rather than a "we will call you back."
## Why this matters
The single most important sentence in any booking conversation is:
> I have availability tomorrow at 2pm or Thursday at 10am. Which works for you?
That sentence, said live on the call against your real calendar, is the difference between a lead that converts immediately and a lead that goes quiet before you can follow up.
***
## Supported calendars
Personal and Workspace accounts
Outlook.com and Microsoft 365
iCloud Calendar
Custom CRM integrations
***
## How it works
### 1. Connect your calendar in one click
OAuth, 30 seconds. We request only what's needed:
* **Read** your free/busy windows, and look up a caller's existing appointment
* **Write** the appointments it books
To offer times, RevDesk reads only whether a window is busy. To find a caller's existing appointment, it searches your calendar for their email or phone number and reads that event's title, time, and attendees. If calendar sync is on for your workspace, RevDesk also stores upcoming events' title, time, description, and location to keep availability current.
### 2. Set your availability
Tell RevDesk when you're available for appointments:
Monday-Friday, 9am-5pm
Different hours on different days
15 minutes between appointments
30-min consultations, 2-hour services
### 3. AI books in real-time
When someone wants to book:
1. AI checks your calendar for the next available slots
2. Offers options to the caller
3. Creates the appointment with all details
4. Sends confirmation to both you and the customer
***
## What gets created
When AI books an appointment, the calendar event includes:
* **Customer name and phone**
* **Service requested**
* **Any notes from the conversation**
* **Link to the full transcript**
You arrive to appointments fully prepared.
***
## Availability settings
### Booking windows
Control how far out people can book:
| Setting | Example |
| - | - |
| Minimum notice | At least 2 hours from now |
| Maximum advance | Up to 30 days out |
| Booking window | Only show next 2 weeks |
### Buffer time
Prevent back-to-back appointments:
* **Before appointments**: 15 minutes to prepare
* **After appointments**: 15 minutes to wrap up
* **Between appointments**: 30 minutes minimum gap
### Blocked times
Mark times as unavailable:
* Lunch breaks
* Personal appointments
* Team meetings
* Holidays
***
## Multiple calendars
If you have multiple team members or service types:
### Option 1: Shared calendar
One calendar that AI books into. Team figures out who takes it.
### Option 2: Round-robin
AI distributes bookings across team members' calendars.
### Option 3: Service-based
Different services book to different calendars:
* "AC Repair" → Technician calendar
* "Consultation" → Sales calendar
***
## Handling conflicts
What if someone books while you're updating your calendar?
RevDesk checks availability at the moment of booking. If a conflict occurs:
1. AI apologizes for the conflict
2. Offers the next available slots
3. Rebooks on the call
The customer never knows there was an issue.
***
## Calendar sync frequency
Your calendar syncs in real-time:
* **New events** appear within seconds
* **Cancellations** free up slots immediately
* **Changes** reflect instantly
No waiting, no stale data.
***
## Setting up calendar sync
Navigate to Settings → Integrations → Calendar
Select "Google Calendar"
Sign in to your Google account and allow access
Select which calendar to use for bookings
Navigate to Settings → Integrations → Calendar
Select "Outlook / Microsoft 365"
Sign in to your Microsoft account and allow access
Select which calendar to use for bookings
Navigate to Settings → Integrations → Calendar
Select "Apple Calendar"
Follow the iCloud authorization flow
Select which calendar to use for bookings
Navigate to Settings → Integrations → CRM
Select your CRM or reach out to Support
Sign in to your CRM account and allow access
Select which calendar to use for bookings
***
## Troubleshooting
1. Check that the blocking event is on the calendar you connected to RevDesk.
2. Confirm that calendar is selected as the booking calendar.
3. Check for time-zone mismatches between RevDesk and the calendar.
1. Make sure you're logged into the correct account.
2. Check that third-party access isn't blocked (common with work accounts).
3. For Google Workspace or Microsoft 365 accounts, your IT admin may need to allow the integration.
1. Check that you selected the correct calendar during setup.
2. Verify the calendar has write permissions.
3. Look for the booking in the RevDesk dashboard to confirm it was created.
Time-zone mismatch. Confirm that your RevDesk account, your calendar, and your business hours all agree on the same time zone.
***
## FAQ
Only what it needs. To offer times, RevDesk checks only whether a window is busy. To find a caller's existing appointment, it searches for their email or phone number and reads that event's title, time, and attendees. If calendar sync is on, it also stores upcoming events' title, time, description, and location.
Delete or cancel the event in your calendar like normal. The customer won't be automatically notified. You'll need to contact them or set up a cancellation automation.
Yes. You can create different booking types with different availability rules.
The AI checks availability at the moment of booking. If you book a slot while it's talking to someone, the AI sees the conflict and offers the next available slot.
Settings → Integrations → Calendar → **Disconnect**. The AI stops booking new appointments until you reconnect.
RevDesk has its own booking system that connects directly to your calendar. You don't need Calendly or similar tools, but they can coexist if you want to keep them.
***
Calendar sync in under a minute.
# Build an integration
Source: https://docs.revdesk.com/developer-guide/build-an-integration
Plan a reliable RevDesk integration around ownership, permissions, write-back, and recovery.
# Build an integration
Start with the operating contract, not the endpoint list. A reliable integration makes it clear which system owns each record, what RevDesk may change, and how both systems recover when a request fails.
## Integration checklist
1. **Name the workflow.** Define the trigger, audience, expected outcome, and human escalation path.
2. **Choose the system of record.** Decide which system owns the customer, opportunity, appointment, and consent state.
3. **Limit access.** Request only the organizations, locations, objects, fields, and actions the workflow needs.
4. **Define identity.** Choose the stable external identifiers used to match and deduplicate records.
5. **Write down write-back.** List every field RevDesk may create or update, including stage, owner, activity, appointment, and next step.
6. **Design retries.** Make repeated requests safe and keep enough context to reconcile a partial failure.
7. **Test the exception path.** Verify missing records, revoked access, invalid data, rate limits, timeouts, and duplicate events.
8. **Add operational visibility.** Record request identifiers, outcome, latency, and the person or workflow responsible for recovery.
## Recommended build path
Follow [Authentication](/api-reference/authentication) and keep credentials server-side.
Review pagination, timestamps, identifiers, and request behavior in [API
conventions](/api-reference/conventions).
Prove one record can enter, receive work, produce an outcome, and update the expected destination.
Use webhooks for relevant changes instead of repeatedly polling broad collections.
Exercise failure cases before expanding the audience or adding more locations.
## Data and identity contract
Write down the contract before sending production data:
* Which organization and, when applicable, location owns each record
* Which system owns customer, opportunity, appointment, consent, and communication state
* The stable identifiers used to match and deduplicate records
* Required, optional, immutable, and sensitive fields
* The direction of each supported write and how conflicts are resolved
* The event or reconciliation process that repairs missed updates
Use organization-scoped credentials and request only the scopes required by this contract. A successful API
request does not grant permission to copy unrelated fields into another system.
## Events and webhooks
Use webhooks when another system needs to react to RevDesk activity without polling. A consumer should accept
the event durably, process it idempotently, and preserve enough evidence to diagnose or replay a failure.
1. Verify the webhook signature before parsing or acting on the payload.
2. Persist the event identifier, organization, event type, received time, and processing state.
3. Return success only after the event is accepted for durable processing.
4. Treat duplicate and out-of-order delivery as normal.
5. Fetch current resource state when the latest state matters more than the event snapshot.
6. Retry transient failures with bounded backoff and route permanent failures to a visible review queue.
Do not place credentials, full payloads, or broad customer records in logs. Use request and event identifiers
to connect sanitized application logs to the affected record.
Verify requests and review supported events.
Handle API failures consistently.
## Recovery and reconciliation
Retries cover temporary failures; reconciliation covers failures you did not observe. Keep a cursor or time
window that lets an operator compare the source and destination, identify missing or conflicting records, and
replay only the affected work. Reconciliation must use the same ownership and idempotency rules as the live
path.
Define who owns each recovery queue, how long failures may remain unresolved, and which corrections require a
person. Never make a broad overwrite the default response to a partial mismatch.
## Testing and production readiness
Test the business outcome across both systems, not only the successful HTTP response.
| Area | Verify |
| - | - |
| Authentication | Valid, expired, revoked, and incorrectly scoped credentials |
| Identity | New record, existing record, duplicate event, and missing external identifier |
| Data | Required fields, invalid values, unexpected nulls, and timezone handling |
| Write-back | Allowed fields change and protected fields remain untouched |
| Reliability | Timeout, rate limit, retry, replay, and partial downstream failure |
| Tenancy | Data never crosses organization or location boundaries |
| Workflow | Stop, opt-out, approval, and human handoff behavior |
| Visibility | Operators can find the request, event, outcome, and recovery owner |
Before production:
1. Run the complete flow with test records.
2. Compare the final state in RevDesk and the connected system.
3. Confirm replaying a request or event does not duplicate work.
4. Verify revoked or insufficient credentials fail closed.
5. Exercise the reconciliation path.
6. Start with a controlled audience or one location and monitor the first production window.
Authenticate and make the smallest complete request before adding the full workflow.
# Developer Guide
Source: https://docs.revdesk.com/developer-guide/index
Build integrations and workflows on RevDesk with clear authentication, data, event, and testing boundaries.
# Build with RevDesk
Use the Developer Guide when you are connecting a system, moving records into or out of RevDesk, subscribing to events, or building against the API and MCP surface.
Authenticate and make your first API request.
Define ownership, field access, write-back, and failure behavior first.
Design reliable event handling and verify webhook requests.
Explore endpoints, schemas, errors, and authentication.
## Pick the right surface
| Need | Start here |
| - | - |
| Read or update RevDesk resources | [API quickstart](/api-reference/quickstart) |
| React to activity and outcomes | [Events and webhooks](/developer-guide/build-an-integration#events-and-webhooks) |
| Connect a supported CRM, calendar, or messaging system | [Integrations](/integrations) |
| Give an AI client controlled access to RevDesk tools | [MCP](/api-reference/mcp) |
| Build realtime calling experiences | [Calling paths](/api-reference/calling-paths) |
Only request the records and actions the workflow needs. Agree on the systems, fields, and write-back
behavior before enabling automation.
# Custom Prompts
Source: https://docs.revdesk.com/getting-started/customization/custom-prompts
Fine-tune how your AI handles specific situations. Go beyond templates with custom instructions.
Custom prompts let you control exactly how your AI behaves in specific situations. Think of them as special instructions for your AI team member.
***
## What are prompts?
Prompts are instructions that guide AI behavior. They're different from the knowledge base: the knowledge base covers *what your business is* (services, hours, policies), while prompts cover *how the AI should behave* (tone, escalation rules, what to always mention).
For the full comparison, see [Knowledge base](/concepts/knowledge-base#knowledge-base-vs-prompts).
***
## Default behavior
Without custom prompts, your AI will:
* Be polite and professional
* Answer questions from your knowledge base
* Book appointments when requested
* Collect information for follow-up when unsure
* Be honest about being an AI when asked
For most businesses, this default behavior works great. Custom prompts let you refine it.
***
## When to use custom prompts
### Emphasize something important
> "Always mention that we offer free estimates when discussing services."
### Handle specific situations
> "If a caller mentions they have an emergency, express urgency and offer same-day service."
### Match your brand voice
> "Use casual, friendly language. Avoid formal phrases like 'I would be happy to assist you.'"
### Set boundaries
> "Never discuss pricing for custom projects over the phone. Offer to schedule a consultation."
***
## How to write good prompts
### Be specific
**Vague:**
> "Be nice to customers."
**Specific:**
> "When a customer expresses frustration, acknowledge their feelings before offering solutions. Say something like 'I understand that's frustrating' before moving on."
### Give examples
**Without example:**
> "Upsell when appropriate."
**With example:**
> "When booking a basic service, mention our premium option: 'We also offer our Complete Care package which includes X and Y for just \$50 more. Would you like to hear about it?'"
### Explain the why
**Without why:**
> "Don't give quotes over the phone."
**With why:**
> "Don't give quotes over the phone because every job is different. Instead say: 'I'd need to have one of our team members take a look before I can give you an accurate quote. Can I schedule a free estimate visit?'"
***
## Common prompt examples
### For empathy
```
When a caller expresses frustration, stress, or urgency:
1. Acknowledge their feelings first ("I understand that's really stressful")
2. Assure them you'll help ("Let's get this sorted out for you")
3. Then move to solving the problem
Never jump straight to solutions without acknowledging emotions.
```
### For upselling
```
After booking a basic service, briefly mention our premium option:
"By the way, many customers also add our [Premium Service] which includes [benefits].
It's just $X more. Would you like me to add that?"
If they decline, don't push. Just say "No problem!" and move on.
```
### For qualifying
```
For all service requests, gather this information:
1. Type of issue (what's the problem?)
2. Property type (residential or commercial?)
3. Urgency level (emergency or can wait?)
4. Best time to reach them
Don't book appointments without knowing at least #1 and #3.
```
### For emergencies
```
If someone mentions: water leak, no heat, no AC, electrical issue, or gas smell:
1. Treat it as urgent
2. Say: "That sounds like it needs immediate attention."
3. Offer same-day or next available emergency slot
4. If after hours, offer to have someone call them within 30 minutes
```
### For competitive situations
```
If a caller mentions they're getting multiple quotes or comparing options:
- Don't be defensive
- Highlight our strengths: "What sets us apart is [key differentiator]"
- Mention our guarantee: "Plus, all our work comes with a 1-year guarantee"
- Make it easy: "Would you like to schedule a free estimate so you can compare?"
```
***
## Adding custom prompts
1. Go to **Settings → AI Behavior → Custom Prompts**
2. Click **Add Prompt**
3. Write your instruction
4. Set priority (when multiple prompts could apply)
5. Save
***
## Prompt priority
When multiple prompts could apply, priority determines which takes precedence:
| Priority | When to use |
| - | - |
| **High** | Critical rules that should always apply |
| **Normal** | General guidelines |
| **Low** | Nice-to-haves that can be overridden |
Example:
* High priority: "Never discuss pricing for jobs over \$10,000"
* Normal priority: "Mention our financing option"
* Low priority: "End calls with 'Have a great day'"
***
## Testing your prompts
After adding prompts:
1. **Call your number** and trigger the scenario
2. **Check if AI follows** the instruction
3. **Review the transcript** for exact wording
4. **Adjust if needed.** prompts often need refinement
### Tips for testing
* Test the exact scenario you wrote the prompt for
* Try edge cases. What happens in similar-but-different situations?
* Have someone else test who doesn't know what to expect
***
## Prompts vs. knowledge base
Sometimes it's unclear where information should go.
**Put in knowledge base if**:
* It's a fact about your business
* Customers might ask about it
* It doesn't require special behavior
**Put in prompts if**:
* It's about how to handle situations
* It involves tone or style
* It's conditional (if X then Y)
***
## Best practices
* Be specific and clear
* Give examples
* Test thoroughly
* Start simple, add complexity later
* Review regularly
* Write vague instructions
* Add too many prompts at once
* Contradict yourself
* Forget to test
* Set it and forget it
***
## FAQ
There's no hard limit, but too many prompts can create conflicts. We recommend starting with 3-5 and adding more only as needed.
Yes. If you have conflicting prompts, the higher-priority one wins. Review your prompts for conflicts regularly.
By default, yes. You can also create prompts that only apply to specific automations (Answer & Route vs. reminder calls).
Yes. Each phone number has separate prompt sections for inbound and outbound calls. The **Custom Prompt** section controls how your AI handles incoming calls, while the **Outbound Prompt** section (under Voice settings) controls the default behavior for outgoing calls made by campaigns and workflows. See [Voice & Personality](/getting-started/setup/voice-personality#outbound-prompt) for details.
In the call transcript, you can see when specific prompts influenced the conversation.
***
Customize how your AI handles situations.
# Custom Sequences
Source: https://docs.revdesk.com/getting-started/customization/custom-sequences
Build automated call sequences that trigger based on events in your business.
Sequences are automated workflows that schedule supported actions from a trigger. They continue only while
enabled and remain subject to workspace, channel, consent, schedule, capacity, and stopping rules.
***
## How Sequences Work
```
Trigger → Wait → Call → Action
```
1. **Trigger**: Something happens (form submitted, appointment booked, etc.)
2. **Wait**: Optional delay (immediately, 5 minutes, 24 hours)
3. **Call**: AI makes the call with your script
4. **Action**: What happens next (book appointment, send notification, update CRM)
***
## Built-In Sequences
These are ready to use. Just turn them on:
**Trigger:** Form submission or new lead
**Action:** An agent contacts the lead on the configured schedule to qualify and book
[Launch guide →](/guides/workflows/qualify-and-book-new-leads)
**Trigger:** Upcoming appointment
**Action:** An agent sends the configured confirmation and reminder steps
[Launch guide →](/guides/workflows/follow-up-and-retention)
**Trigger:** Missed appointment
**Action:** An agent follows the configured recovery and rescheduling path
[Launch guide →](/guides/workflows/follow-up-and-retention)
**Trigger:** Completed appointment
**Action:** An agent follows the configured feedback and review path
[Launch guide →](/guides/workflows/follow-up-and-retention)
***
## Creating Custom Sequences
### Step 1: Choose Your Trigger
| Trigger Type | When It Fires |
| - | - |
| Form Submission | When someone fills out a form on your website |
| New Lead (CRM) | When a lead is created in HubSpot, etc. |
| Appointment Booked | When any appointment is scheduled |
| Appointment Completed | When an appointment is marked complete |
| Appointment Missed | When someone no-shows |
| Manual | When you trigger it from the dashboard |
| Webhook | When you send a POST request to your sequence URL |
### Step 2: Set the Timing
* **Immediately**: Queue the action as soon as the configured schedule and capacity allow
* **Delay**: Wait X minutes/hours/days before calling
* **Window**: Only call during business hours (queue for next morning if after-hours)
### Step 3: Define the Conversation
Tell the AI what to accomplish:
```
Goal: Confirm the appointment and answer any questions.
Start with: "Hi, this is [Your Business]. I'm calling to confirm your
appointment on [date] at [time]."
If they want to reschedule: Offer alternative times from the calendar.
If they want to cancel: Mark as cancelled and ask if they want to rebook later.
If they have questions: Answer from the knowledge base.
```
### Step 4: Set the Outcome Actions
What happens after the call:
* Update the calendar (reschedule, cancel, confirm)
* Send a notification (email, SMS, Slack)
* Update the CRM
* Trigger another sequence
***
## Example: Lead Qualification Sequence
**Scenario:** A roofing company wants to qualify leads before sending an estimator.
```yaml theme={null}
Trigger: Form submission on "Free Estimate" page
Timing: As soon as the configured schedule and capacity allow
Conversation goal: Qualify the lead and book an estimate if qualified.
Questions to ask:
- What type of roof do you have? (shingle, tile, metal)
- How old is the roof?
- Are you the homeowner?
- What's your timeline for getting this done?
Qualification criteria:
- Must be homeowner
- Roof must be 10+ years old OR have visible damage
- Timeline within 3 months
If qualified: Book an estimate appointment
If not qualified: Thank them and add to nurture list
```
***
## Sequence Settings
### Call Attempts
* **Max attempts**: How many times to try if no answer (1-5)
* **Retry delay**: How long to wait between attempts (5 min - 24 hours)
### Caller ID
* Use your main business number
* Use a specific number for this sequence
* Rotate between numbers
### Voicemail
* Leave a voicemail if no answer
* No voicemail, just retry
* Custom voicemail message
### Hours
* Any time calling-hour rules allow
* Business hours only
* Custom schedule
***
## Tips for Effective Sequences
Start with one goal per sequence. "Confirm appointment" or "Qualify lead," not both.
Speed to lead calls should be immediate. Reminder calls should be 24 hours before. Review requests work
best 1-2 hours after service.
Use test mode to receive the calls yourself. Make sure the conversation flows naturally.
Check completion rates. If calls are going unanswered, try different times. If conversations are ending
early, adjust the script.
***
## Limits
Sequence limits and included minutes depend on your plan. [Contact sales](https://cal.com/revdesk/30min).
***
Our team can help you design sequences for your specific use case.
# FAQ
Source: https://docs.revdesk.com/getting-started/faq
Frequently asked questions about RevDesk. Find answers to common questions about setup, features, billing, and more.
# Frequently Asked Questions
Quick answers to the most common questions about RevDesk.
***
## Getting Started
Most businesses are live the same day. Add your business info, connect your calendar, and you're ready to take calls.
No. RevDesk is designed for business owners, not developers. If you can describe what you want in plain
English, you can use RevDesk.
Yes. Get started at [revdesk.com/trial](https://www.revdesk.com/trial) for a 14-day RevDesk Sandbox with one
test number, verified recipients, and a \$20 hard allowance. Conversion preserves configuration while
removing test activity by default.
At minimum: your business name, what you do, and your business hours. You can add more detail later.
***
## Phone Numbers
Yes. You can either forward your existing number to RevDesk, or port it over entirely. Most businesses start with forwarding.
[Contact sales](https://cal.com/revdesk/30min) for phone number pricing by type (local or toll-free). Most
businesses only need one to start.
Yes. Add as many as you need for different departments, locations, or marketing campaigns.
Yes. Pick any available area code, or get a toll-free number.
***
## AI Capabilities
No. Modern voice AI sounds natural and conversational. Try our demo to hear for yourself.
42 languages: English, Spanish, French, German, Portuguese, Italian, Chinese, Japanese, Korean, Hindi,
Arabic, Russian, Dutch, Polish, Swedish, Turkish, Danish, Norwegian, Finnish, Greek, Czech, Romanian,
Hungarian, Slovak, Bulgarian, Ukrainian, Croatian, Hebrew, Thai, Vietnamese, Indonesian, Malay, Tagalog,
Bengali, Tamil, Telugu, Marathi, Gujarati, Kannada, Malayalam, Punjabi, and Georgian. Auto-detect covers
ten major languages, including English and Spanish.
Yes. Connect your calendar and the AI will check your real availability and book appointments directly.
It will acknowledge that it doesn't have that information, collect the caller's contact info, and let them
know someone will follow up. You get notified immediately.
No. If asked directly, the AI will always be honest about being an AI assistant. However, it won't randomly
announce it unless relevant.
Yes. Define rules for when transfers happen (emergencies, VIP customers, specific requests), and the AI will transfer accordingly.
***
## Calendar & Booking
Google Calendar, Microsoft Outlook (including 365), and Apple Calendar.
No. The AI checks your real-time availability before offering any time slots.
Yes. Create different booking types with different availability rules.
Only what it needs. To offer times, RevDesk checks only whether a window is busy. To find a caller's existing appointment, it reads that event's title, time, and attendees. If calendar sync is on, it also stores upcoming events' details to keep availability current.
***
## Pricing & Billing
RevDesk is configured by physical location, enabled systems, telecom, expected usage, and implementation
needs. Onboarding and your final quote show the commercial lines that apply. See the current
[pricing page](https://www.revdesk.com/pricing) for available plans and offers.
Additional minutes use the volume rate stated in your quote. There is no mid-conversation service
interruption, and usage remains visible in the dashboard.
Your quote and billing view show the plan, enabled services, included usage, and applicable overage or
add-on charges. Contact support before enabling a service if its billing unit is unclear.
Workspace administrators can manage agents that are available for the enabled product. Review the
confirmation shown in the app for any billing or effective-date change.
Yes. Annual software prepay is available. Your quote shows the annual benefit and keeps phones and usage on
a monthly cadence.
***
## Features & Integrations
RevDesk documents supported connections for HubSpot and LeadConnector, plus Zapier, webhooks, and the
public API for custom workflows. **Settings → Integrations** shows what your
workspace can connect.
See [Integrations](/integrations).
Yes. Get real-time notifications via email, SMS, Slack, or webhook.
Recording and transcription depend on the call path, workspace configuration, applicable law, and enabled
product. When present, authorized workspace members can open them from the call or conversation record.
Yes. Customize greetings, scripts, and specific responses. You can also add custom prompts to guide AI behavior in specific situations.
***
## Security & Privacy
Review [Security](/policies/security) for current encryption, access-control, subprocessor, retention, and
compliance information.
Access is limited through workspace permissions and the service providers required to operate RevDesk, as
described in the [Security](/policies/security) and canonical privacy documentation.
Available deletion and retention controls depend on the record and workspace policy. Contact support for a
request that is not available in the product, subject to legal and contractual retention requirements.
RevDesk supports eligible HIPAA workloads under the required configuration and agreement. See
[HIPAA & BAA coverage](/concepts/hipaa) before using protected health information.
***
## Troubleshooting
Check that:
1. Your phone number is active
2. Forwarding is set up correctly (if using existing number)
3. Your account is in good standing
If issues persist, contact support.
Review your knowledge base for the incorrect information and update it. Changes take effect immediately.
Verify your calendar is properly connected in Settings → Integrations. Try disconnecting and reconnecting.
Check your notification settings in Settings → Notifications. Also check spam folders for email notifications.
***
## Still have questions?
Live support during business hours
[support@revdesk.com](mailto:support@revdesk.com)
See RevDesk in action
# Call Capacity Planning
Source: https://docs.revdesk.com/getting-started/features/call-capacity
Understand how phone numbers and dedicated lines control your outbound call throughput. Plan your setup by budget or goal.
## How outbound capacity works
Two things control how fast you can dial:
1. **Phone numbers.** Each can make up to **75 calls per day**, the limit that protects your number reputation with carriers.
2. **Dedicated concurrent lines.** Contact us for high volume.
Think of phone numbers as your daily fuel. Dedicated lines are the number of lanes open on the highway.
***
## Phone number capacity
Each phone number is limited to protect your caller ID reputation:
* **75 calls per day** per number
* **20 calls per hour** per number (burst protection)
These are industry best practices enforced automatically. Numbers that exceed these limits risk being flagged as spam by carriers.
| Phone Numbers | Calls/Day | Calls/Month |
| - | - | - |
| 1 | 75 | 2,250 |
| 3 | 225 | 6,750 |
| 5 | 375 | 11,250 |
| 10 | 750 | 22,500 |
| 25 | 1,875 | 56,250 |
| 50 | 3,750 | 112,500 |
We recommend **numbers with local area codes**. Contacts are far more likely to answer a call from a
familiar area code.
***
## Dedicated concurrent lines
Each dedicated line gives you one guaranteed simultaneous outbound call. Without dedicated lines your calls share a managed pool with other customers, which can queue during peak times.
| Lines | Simultaneous Calls |
| - | - |
| 5 | 5 |
| 10 | 10 |
| 25 | 25 |
| 50 | 50 |
When all lines are busy, new calls queue automatically and dispatch as lines free up. TCPA compliance and DNC filtering apply throughout.
***
## Plan by budget
[Contact sales](https://cal.com/revdesk/30min) to scope phone numbers, dedicated lines, and usage for your budget.
***
## Plan by goal
Know how many calls you need? Work backwards:
| Target Calls/Month | Numbers Needed | Lines Needed |
| - | - | - |
| 2,500 | 2 | 1 |
| 5,000 | 3 | 2 |
| 10,000 | 5 | 3 |
| 25,000 | 12 | 6 |
| 50,000 | 23 | 12 |
| 100,000 | 45 | 23 |
These estimates assume campaigns run during the TCPA calling window (8am-9pm, 13 hours) with an average call
duration of 3 minutes.
***
## What happens at the limits
### When numbers are exhausted
If all your phone numbers hit their daily limit mid-campaign:
* Remaining contacts are set to **pending**.
* The campaign **resumes the next day** automatically.
* Pending contacts stay queued until they dial out.
### When lines are full
If all your concurrent lines are busy:
* New calls **queue automatically**
* They dispatch as soon as a line frees up
* Order is maintained
### Number health monitoring
RevDesk monitors the answer rate of each phone number over the past 7 days. If a number's answer rate drops below healthy levels (indicating possible spam flagging), it's automatically removed from rotation.
***
## Recommendations
Start with **3 premium numbers** and **3 dedicated lines**. That covers 225 calls per day, enough for most small campaigns while you build number reputation.
For weekly campaigns of 500-2,000 contacts, get **10 numbers + 10 lines**. You'll reach 750 contacts/day and
finish most campaigns in 1-3 days.
For 5,000+ contacts/week, plan for **25+ numbers + 25+ lines**. This gives 1,875 calls/day capacity.
Consider spreading large campaigns across multiple days for best answer rates.
Contacts are more likely to answer a local area code than a random or toll-free number. Pick an area code that matches your target market.
***
## The math
```
Daily capacity = phone numbers x 75
Monthly capacity = daily capacity x 30
Numbers needed = ceil(target calls per day / 75)
Lines needed = ceil(numbers / 2) (for typical 3-min calls)
```
***
Add numbers to increase your daily capacity
Add lines for guaranteed concurrent calls
# Compliance & Regulations
Source: https://docs.revdesk.com/getting-started/features/compliance
Understand calling regulations and stay compliant. TCPA, DNC, and best practices.
Phone calls and texts are regulated. This guide helps you stay compliant.
***
This is educational information, not legal advice. Consult a lawyer for specific compliance questions.
***
## Key regulations
### TCPA (Telephone Consumer Protection Act)
The main US law governing phone calls and texts:
* **Consent required** for marketing calls/texts
* **Time restrictions** on when you can call
* **Do-not-call compliance** required
* **Penalties** up to \$1,500 per violation
### State laws
Many states have additional requirements:
* **California** - Stricter consent requirements
* **Florida** - Written consent for texts
* **New York** - Enhanced caller ID requirements
Check laws in states where you call.
***
## Consent requirements
### For marketing calls
You need **prior express consent**:
* Customer gave you their number
* They knew you might call
* They agreed to receive calls
### For marketing texts
You need **prior express written consent**:
* Customer explicitly agreed to texts
* Agreement was documented
* They knew what they'd receive
### For informational calls
Lower threshold:
* Appointment reminders (existing relationship)
* Order updates (transactional)
* Service notifications (non-marketing)
Still respect opt-outs and calling hours.
***
## Calling hours
**Federal:** 8 AM - 9 PM (recipient's time zone)
**Best practice:** 9 AM - 8 PM
| Time | Compliant? | Recommended? |
| - | - | - |
| 7 AM | No | No |
| 9 AM | Yes | Yes |
| 12 PM | Yes | Maybe (lunch) |
| 6 PM | Yes | Yes |
| 9 PM | Yes | Borderline |
| 10 PM | No | No |
RevDesk automatically respects time zones.
***
## Do-not-call (DNC)
### National DNC Registry
Check numbers against the national list:
* **Before calling** - Scrub your list
* **Every 31 days** - Re-scrub regularly
* **Honor requests** - Add to internal DNC immediately
### Internal DNC list
When someone says "don't call me":
* **Remove immediately** - Within 24 hours
* **Across all campaigns** - Not just the current one
* **Keep records** - Document when they opted out
RevDesk manages your DNC list automatically.
***
## Recording disclosure
Most states require one-party consent (you can record if you're on the call). But some require all-party consent:
**All-party consent states:**
* California, Connecticut, Delaware, Florida, Illinois, Maryland, Massachusetts, Michigan, Montana, Nevada, New Hampshire, Oregon, Pennsylvania, Washington
**Best practice:** Always announce recordings.
```
"This call may be recorded for quality purposes."
```
Default greetings include a recording disclosure. Keep it if you write a custom greeting.
***
## Caller ID
Rules for displaying caller ID:
* **Don't spoof** - Must be a number you control
* **Be reachable** - Number must work if called back
* **Be identifiable** - Ideally shows your business name
See [Branded Caller ID](/getting-started/setup/phone-numbers#branded-caller-id).
***
## Text message compliance
Special rules for SMS:
* **Clear identification** - Include business name
* **Opt-out instructions** - "Reply STOP to unsubscribe"
* **Honor STOP immediately** - No more messages
* **Frequency disclosure** - Tell them how often you'll text
* **Data rates disclaimer** - "Msg & data rates may apply"
***
## RevDesk compliance features
Built-in tools to help you stay compliant:
* Automatic DNC list management
* Time zone-aware calling hours
* Recording disclosure announcements
* Consent tracking
* Opt-out handling (STOP for texts)
* Caller ID verification
* Campaign logging and records
***
## Record keeping
Keep records of:
| Record | Retention |
| - | - |
| Consent documentation | 4+ years |
| DNC requests | 5+ years |
| Call logs | 2+ years |
| Opt-out requests | 5+ years |
RevDesk stores these automatically.
***
## Best practices
Don't assume consent. Have explicit opt-in for marketing calls/texts.
Start calls with who you are and why you're calling.
Don't hide the unsubscribe. Make it simple.
Just because you can call doesn't mean you should. Be reasonable.
Keep records of consent, opt-outs, and compliance measures.
***
## If you receive a complaint
1. **Take it seriously** - Investigate promptly
2. **Document** - Record the complaint and your response
3. **Remediate** - Fix the issue
4. **Add to DNC** - Immediately stop calling that number
5. **Review processes** - Prevent recurrence
***
## Compliance Center widget
The Outreach page includes a persistent **Compliance Center** button:
* **Amber warning triangle** until the workspace acknowledges TCPA/DNC obligations.
* **Green shield** once acknowledged. Annual re-acknowledgement prompt at 30 days before expiry.
Open it to see, in one place:
* Workspace acknowledgement status (and who acknowledged, when).
* Active disclosures (recording, SMS STOP append, TCPA quiet hours, DNC scrubbing, two-party-consent state list).
* Per-active-campaign checkmarks: terms accepted, recording disclosure attested, calling-window configured, frequency cap set, 30-day attestation rate.
* Consent record counters (voice / SMS / email opt-in, inbound-originated) and recent consent events.
* 30-day disclosure attestation rates for voice + first-touchpoint SMS.
* "Export full audit": CSV with channel, source, timestamp, sender, use case, disclosure attestation, two-party-state flag.
## Per-channel consent records
When you (the workspace operator) record an opt-in for a contact, RevDesk persists:
| Field | Description |
| - | - |
| `{channel}OptIn` | Boolean per channel (voice, SMS, email). |
| `{channel}OptedInAt` | Timestamp the opt-in was captured. |
| `{channel}ConsentSource` | Origin of consent (e.g. `csv_upload`, `manual_admin`, `inbound_initiated`). |
| `{channel}ConsentSender` | The user / system actor that captured the consent. |
| `{channel}ConsentUseCase` | Free-form use-case label (e.g. `sales_followup`). |
| `firstInbound{Channel}At` | First time the contact initiated inbound on that channel. |
When a contact initiates an inbound call or SMS, an **inbound-initiated** opt-in is recorded automatically with `ConsentSource = "inbound_initiated"`.
Capture opt-ins programmatically via the partner API, on contact CSV import, or manually from the contact-detail page.
## Frequency cap
Each outbound campaign has a `dailyCallsPerContact` setting (default `1`). Calls to a contact past the cap are deferred to local midnight of the next day. Admin TCPA bypass does **not** bypass the frequency cap.
## Recording disclosure attestation
Every outbound call records:
* `disclosureAttested`: true when the agent's live greeting contained the recording disclosure phrase.
* `disclosureText`: the disclosure phrase actually attached (first sentence of the greeting).
* `twoPartyStateAtCallTime`: true when the recipient's area code maps to an all-party-consent state.
Powers the Compliance Center "Last 30 days" attestation rate.
***
## Resources
* [FCC TCPA Guide](https://www.fcc.gov/general/telemarketing-and-robocalls)
* [FTC Do Not Call](https://www.donotcall.gov)
* [National DNC Registry](https://www.donotcall.gov/register)
* [Outreach audit reference](/concepts/outreach-audit)
***
Configure compliance features
# Inbox
Source: https://docs.revdesk.com/getting-started/features/inbox
Every call, message, and conversation in one place. Full transcript, recording, and AI summary attached to each one.
Every inbound call, outbound call, SMS thread, and voicemail lands in the same inbox. Each conversation carries a recording, a full transcript, an AI summary, and a structured outcome.
When supported identity or enrichment data is available, the inbox can attach it to the conversation. Treat
that context as a lead for review rather than verified identity, eligibility, or consent.
## What you see
* **All inbound calls.** Every conversation the AI handled.
* **Outbound calls.** Calls placed on your behalf, including campaigns and Lead Response.
* **SMS threads.** Two-way text conversations.
* **Voicemails.** Messages left when AI was not able to complete a call.
## Conversation threads
Each contact has a complete history.
```
John Smith - (555) 123-4567
├── Jan 15, 2:30 PM - Inbound call (3:45)
│ └── Booked appointment for Jan 20
├── Jan 14, 10:00 AM - SMS sent
│ └── Appointment reminder
└── Jan 10, 9:15 AM - Inbound call (2:12)
└── Initial inquiry about services
```
Click any conversation to open the transcript, recording, and AI summary.
## Filtering
| Filter | What it shows |
| - | - |
| All | Everything |
| Calls | Phone calls only |
| Messages | SMS only |
| Unread | Not yet reviewed |
| Flagged | Marked for follow-up |
| Date range | A specific window |
## Taking action
From any conversation:
* **Listen to the recording.** Hear the call as it happened.
* **Read the transcript.** Text version with speaker labels and timestamps.
* **Add notes.** Internal notes for your team.
* **Flag for follow-up.** Mark for attention.
* **Assign to a teammate.** Route to the right person.
## Real-time updates
The inbox is live. New conversations appear without a refresh, and active calls stream a live transcript as they happen. Push notifications route to your phone for events you mark important.
## Transcripts and recordings
When recording is on, each call carries:
* **Full audio recording.** The complete conversation, encrypted at rest.
* **Transcript.** Text with speaker labels and timestamps.
* **AI summary.** A one-paragraph summary of what happened and what was decided.
* **Outcome.** Structured: booked, transferred, voicemail, escalated, and so on.
### What a transcript looks like
```
[0:00] AI: Good morning, thanks for calling Smith HVAC.
How can I help you today?
[0:05] Caller: Hi, my AC stopped working. It's really hot.
[0:09] AI: Sorry to hear that. Let me get a tech out to you.
We have tomorrow morning or Thursday afternoon.
Which works?
[0:18] Caller: Tomorrow morning.
[0:21] AI: Booked. Address for the service call?
```
Click any timestamp in the dashboard to jump to that part of the recording.
### Searching across calls
Search runs across every transcript in your inbox.
| Search for | Examples |
| - | - |
| Keywords | "emergency", "pricing", "complaint" |
| Caller name | "John Smith" |
| Phone number | "555-123-4567" |
| Date range | Last 7 days, this month |
| Outcome | Booked, transferred, voicemail |
### Recording controls
* **Playback speed.** 0.5×, 1×, 1.5×, 2×.
* **Skip silence.** Jump past pauses.
* **Download.** Save the audio file.
* **Share.** Send a link to a teammate.
### Accuracy
Transcription is high-accuracy but not perfect. Proper nouns (names, businesses) and industry-specific terms occasionally need review. Heavy accents can be misheard. You can edit a transcript if it matters for your records.
### Privacy and retention
Recordings are encrypted at rest. Available retention, deletion, and export controls depend on workspace
configuration and applicable legal or contractual requirements. See [Security](/policies/security) for the
current platform posture.
Recording laws vary by state and country. Default greetings include a recording disclosure. Keep it if you
write a custom greeting.
## Best practices
Skim new conversations once a day. Watch for questions the AI couldn't answer and add the answers to your
knowledge base.
Build a simple flagging system. Red for urgent follow-up, yellow for review.
Internal notes you write today save your future self time when the same contact calls back.
See every conversation
Use what you hear to teach the AI
# Insights & Analytics
Source: https://docs.revdesk.com/getting-started/features/insights
Understand how your AI is performing. See call volumes, conversion rates, and trends over time.
## Dashboard overview
Your dashboard shows key metrics at a glance:
| Metric | What it tells you |
| - | - |
| **Total calls** | Volume of calls handled |
| **Answer rate** | % of calls AI answered |
| **Avg call duration** | How long conversations last |
| **Booking rate** | % of calls that resulted in appointments |
| **Transfer rate** | % of calls transferred to humans |
***
## Key performance indicators
### Call volume
Track calls over time:
* **By hour** - See peak calling times
* **By day** - Identify busy days
* **By week/month** - Spot trends
### Conversion metrics
* **Lead → Appointment** - How many callers book
* **Inquiry → Conversion** - How many questions lead to action
* **Repeat callers** - Customer return rate
### Quality metrics
* **Caller satisfaction** - Post-call surveys (if enabled)
* **Resolution rate** - Issues handled without transfer
* **Knowledge gaps** - Questions AI couldn't answer
***
## Reports
Generate reports for:
Calls, bookings, and highlights from today
Week-over-week comparisons and trends
Full breakdown with charts and analysis
Build your own with selected metrics
***
## Filtering data
Drill down into specific segments:
* **Date range** - Any time period
* **Phone number** - Specific line
* **Call outcome** - Booked, transferred, missed
* **Service type** - If you track services
* **Team member** - If using round-robin
***
## Trend analysis
Spot patterns in your data:
* **Seasonal trends** - Busy seasons vs. slow periods
* **Day-of-week patterns** - Monday rush, Friday slowdown
* **Time-of-day patterns** - Morning vs. afternoon
* **Before/after comparisons** - Impact of changes
***
## Alerts
Set up notifications for:
* **Volume spikes** - More calls than usual
* **Low booking rate** - Conversion drops below threshold
* **High transfer rate** - AI transferring too often
* **Knowledge gaps** - New questions appearing
***
## Exporting data
Export your data in multiple formats:
* **CSV** - For spreadsheets
* **PDF** - For sharing reports
* **API** - For custom integrations
***
## Understanding your numbers
It varies by industry and lead source. Compare your own sources over time.
Common causes: Questions AI can't answer (expand knowledge base), callers specifically requesting humans (normal), complex issues (adjust when to transfer).
Shorter isn't always better. If calls are too short, AI might not be engaging. Too long might mean inefficiency. Aim for efficient, complete conversations.
***
See your performance dashboard
# Messaging channels
Source: https://docs.revdesk.com/getting-started/features/messaging
Configure supported customer messaging channels and keep conversations, consent, and handoffs attached to the right contact.
RevDesk can continue customer work across supported voice and messaging channels. Availability depends on the
workspace, country, sender, recipient capability, consent, and enabled integrations. Team notifications such
as Slack are separate from customer messaging.
## Why channel choice matters
Choose a channel based on customer preference, recipient capability, the purpose of the message, and the
consent attached to the contact. Do not assume that an available phone number is eligible for every channel.
## Channel capabilities
| Channel | Two-way | Rich content | Broadcast templates | Typical use |
| - | - | - | - | - |
| iMessage | ✓ | Booking cards, tapbacks, read receipts | ✓ | Follow-up, reminders, confirmations |
| WhatsApp | ✓ | Templates, media, one-tap replies | ✓ | International + high-engagement follow-up |
| SMS | ✓ | Links, keywords | ✓ | Universal fallback, compliance keywords |
| Email | ✓ | Full HTML, attachments | ✓ | Recaps, documents, email-to-call triggers |
| Telegram | ✓ | Media, buttons | — | Customer conversations when the integration is enabled |
| Slack | — | Cards, threads | — | Internal notifications and escalation |
## One contact timeline
* **Same memory.** A voice call, an iMessage reply, and an email open all land on the same contact timeline. The agent carries full context across channels.
* **Channel-aware delivery.** A workflow may select or fall back between channels only when the sender,
recipient, account, and consent state support it.
* **Team handoff.** When a person should take over, the conversation remains visible in the inbox and may
notify an enabled team integration.
## Compliance, synced
Consent, quiet hours, and opt-outs are enforced per contact:
* A phone opt-out stops calls, SMS, and iMessage to that number. Email and WhatsApp opt-outs apply to that
channel.
* Broadcasts respect TCPA calling windows and per-state rulebooks.
* WhatsApp and iMessage business messaging follow each platform's template and policy rules. RevDesk submits WhatsApp templates; Meta approves them. iMessage uses a line set up for your workspace.
## Set up your channels
Blue-bubble follow-up with booking cards and read receipts.
WhatsApp Business templates and two-way replies.
Registered 10DLC texting with compliance keywords.
Confirmations, recaps, and email-to-call triggers.
Customer conversations on Telegram.
Escalations and daily digests where your team lives.
# Round-Robin Distribution
Source: https://docs.revdesk.com/getting-started/features/round-robin
Distribute calls and bookings evenly across your team.
Automatically distribute calls and appointments across team members. Keep workloads balanced.
***
## What is round-robin?
Instead of all calls going to one person, round-robin distributes them:
```
Call 1 → Alice
Call 2 → Bob
Call 3 → Carol
Call 4 → Alice (back to start)
...
```
This ensures fair distribution and prevents burnout.
***
## Distribution methods
### Simple round-robin
Each person gets the next call in sequence:
* **Pros:** Perfectly equal distribution
* **Cons:** Doesn't account for availability
### Availability-based
Only routes to available team members:
* **Pros:** Respects calendars and working hours
* **Cons:** Some may get more if others are busy
### Weighted round-robin
Some team members get more:
```
Alice: 40% (senior, higher capacity)
Bob: 30%
Carol: 30%
```
* **Pros:** Account for experience/capacity
* **Cons:** Requires more setup
### Least recent
Routes to whoever hasn't had a call in longest:
* **Pros:** Natural balancing
* **Cons:** May not respect preferences
***
## Setting up round-robin
Go to Settings → Team → Create Team
Select who should be in the rotation
Pick your distribution style
Route calls to this team instead of individual
***
## Configuration options
### Working hours
Respect each member's schedule:
* **Only route during working hours**
* **Skip members who are marked away**
* **Account for lunch breaks**
### Capacity limits
Set maximum appointments per person:
```
Alice: Max 8 calls/day
Bob: Max 6 calls/day
Carol: Max 10 calls/day
```
Once capacity is hit, skip to next person.
### Priority routing
Some calls go to specific people first:
```
VIP customers → Senior team member first
New leads → Sales team
Support → Support team
```
***
## Round-robin for bookings
When customers book appointments, distribute across team:
1. **Show combined availability** - All team members' open slots
2. **Customer picks time** - Any available slot works
3. **Assign automatically** - System picks who gets it
Or let customers choose:
1. **Show team members** - Display available people
2. **Customer picks person** - They choose who they want
3. **Book directly** - Goes to chosen calendar
***
## Monitoring distribution
Track how calls are distributed:
| Team Member | This Week | This Month | All Time |
| - | - | - | - |
| Alice | 12 calls | 48 calls | 520 calls |
| Bob | 10 calls | 45 calls | 498 calls |
| Carol | 11 calls | 47 calls | 512 calls |
Look for imbalances and adjust weights if needed.
***
## Handling unavailability
When someone is unavailable:
* **Skip them** in the rotation
* **Try next person** automatically
* **Queue for later** if no one available
* **Notify admin** if all unavailable
***
## Use cases
### Sales team
Distribute leads evenly:
```
New lead comes in → Assign to salesperson
→ They follow up → Track who converts best
```
### Service technicians
Balance service calls:
```
Customer books → Assign to available tech
→ Based on location, skill, capacity
```
### Support team
Route support calls:
```
Support call → First available agent
→ Based on who answered least recently
```
***
## Best practices
3-7 people per rotation works best. Larger teams make it harder to track.
Check distribution reports. Adjust weights if someone's overloaded.
Mark team members away when they're out. The system will skip them.
If your team spans zones, set working hours per member.
***
Configure team distribution
Learn about team management
# SMS & Messaging
Source: https://docs.revdesk.com/getting-started/features/sms
Text your customers automatically. Send reminders, confirmations, and follow-ups via SMS.
SMS is available only when the workspace, sending number, recipient, country, registration, and consent state
support it. See [Messaging channels](/getting-started/features/messaging) before configuring cross-channel
fallback behavior.
## What you can do
* **Send automated texts** - Confirmations, reminders
* **Receive texts** - Customers can text your number
* **AI responses** - Intelligent auto-replies
* **Templates** - Pre-written messages for common scenarios
***
## Automated messages
### Appointment confirmation
Sent immediately after booking:
```
Hi [Name]! Your appointment with [Business] is
confirmed for [Date] at [Time].
Reply YES to confirm or call us to reschedule.
```
### Missed-call text-back
Optionally text an inbound caller when the call ends unanswered. The workspace default is disabled;
when an admin enables it, RevDesk presets a 30-second delay. Each phone number can inherit the
workspace setting or override the enabled state, message, and delay.
Before sending, RevDesk rechecks that the call was inbound, terminal, never answered, and not
voicemail. It also rechecks business and quiet hours, consent and DNC status, A2P registration,
wallet balance, channel availability, opt-outs, and human takeover. The text is written to the
normal inbox thread, just like a human or agent message.
### Follow-up
Sent after service:
```
Thanks for choosing [Business]! We'd love your
feedback. How was your experience today?
Reply 1-5 (1=Poor, 5=Excellent)
```
***
## Message templates
Create reusable templates:
| Template | Use case |
| - | - |
| **Booking confirmation** | After appointment scheduled |
| **Missed-call text-back** | After an eligible unanswered inbound call |
| **On our way** | Technician en route |
| **Follow-up** | After service completed |
| **Review request** | Ask for reviews |
***
## Variables
Personalize messages with variables:
| Variable | Replaced with |
| - | - |
| `[Name]` | Customer's name |
| `[FirstName]` | First name only |
| `[Date]` | Appointment date |
| `[Time]` | Appointment time |
| `[Business]` | Your business name |
| `[Address]` | Your address |
| `[Phone]` | Your phone number |
***
## AI-powered responses
When customers text back, your AI can:
* **Answer questions** - Using your knowledge base
* **Reschedule** - Move appointments
* **Confirm** - Mark attendance confirmed
* **Escalate** - Alert you for complex issues
Example conversation:
```
Customer: Can I change my appointment to Thursday?
AI: Of course! I have availability Thursday at
10 AM, 2 PM, or 4 PM. Which works best for you?
Customer: 2pm
AI: Perfect! I've rescheduled you for Thursday at
2 PM. You'll receive a confirmation shortly.
```
***
## Two-way texting
Your RevDesk number can send and receive texts:
* **Inbound texts** appear in your inbox
* **Reply from dashboard** or let AI handle it
* **Full conversation history** with each contact
***
## SMS compliance
Text messaging requires consent. Make sure customers have opted in to receive texts from you.
Best practices:
* **Get consent** - Checkbox on forms, verbal agreement
* **Include opt-out** - "Reply STOP to unsubscribe"
* **Honor opt-outs** - Immediately stop messaging
* **Identify yourself** - Include your business name
***
## Message pricing
SMS is billed per segment. A segment holds up to 160 characters (70 for emoji/Unicode); longer messages span multiple segments. [Contact sales](https://cal.com/revdesk/30min) for domestic and international messaging rates.
***
## Setting up SMS
Go to Settings → Messaging → Enable SMS
Register your brand and campaign (A2P 10DLC). Unregistered sends are blocked.
Create or customize messages, including the optional missed-call text-back
Choose when messages are sent automatically
Send a test message to yourself
***
Set up your message templates
Understand SMS regulations
# Tasks and lead sources
Source: https://docs.revdesk.com/getting-started/features/tasks-and-lead-sources
Connect lead sources once, route each delivery into one or more tasks, and monitor delivery health.
Lead sources are the durable connections that bring new leads into RevDesk. Tasks define what an agent should do when a source delivers a lead. Keeping those decisions separate lets one source feed several tasks, and lets each task own its team, agent, outcome, and guardrails.
## Create a lead source
Open **Directory → Lead sources**. Workspace admins can create and administer sources; members can select sources they are allowed to use without seeing credentials.
Select ADF email/webhook, generic webhook, CRM/provider, or a RevDesk hosted form.
Give the source a recognizable name and, when applicable, assign its dealership location. Leave location
empty for a workspace-wide source.
Match incoming fields to phone, name, email, country, external ID, and source. Nested JSON fields use dot
paths such as `contact.phone`.
Copy the generated address and secret into the provider, then send a live test. The card shows the latest
delivery, test result, and any mapping or authentication error.
Each source card shows its provider and format, location, enabled state, intake address, recent delivery health, test status, and connected tasks. Disabling or archiving a source stops new deliveries without changing its history.
## Generic webhook
Send JSON or URL-encoded form data to the source-specific endpoint:
```text theme={null}
POST https://app.revdesk.com/api/webhooks/leads/{intakeToken}
```
The intake token in the URL identifies the source and is a credential. Keep the URL private. A connection can also authenticate with the source secret as a Bearer token:
```bash theme={null}
curl -X POST "https://app.revdesk.com/api/webhooks/leads/$REVDESK_INTAKE_TOKEN" \
-H "Authorization: Bearer $REVDESK_INTAKE_SECRET" \
-H "Content-Type: application/json" \
-d '{
"lead_id": "lead_123",
"name": "Jane Smith",
"phone": "+14155551234",
"email": "jane@example.com",
"source": "website"
}'
```
For providers that sign requests, send the lowercase hex HMAC-SHA256 of the raw request body in `X-RevDesk-Signature` (an optional `sha256=` prefix is accepted). The HMAC key is the source secret.
### Payload mapping
Without a custom map, RevDesk recognizes common names including `phone`, `phone_number`, `mobile`, `name`, `first_name`, `last_name`, `email`, `external_id`, `lead_id`, `country`, and `source`. A saved map takes priority and may point to nested values:
```json theme={null}
{
"phone": "contact.mobilePhone",
"name": "contact.displayName",
"email": "contact.primaryEmail",
"externalId": "event.leadId"
}
```
Phone is the key field for call and message work. Always send E.164 when possible, such as `+14155551234`. The original payload is retained with the intake attempt for troubleshooting and task context.
### Responses and health
* `200` means the delivery was accepted and reports the ingestion outcome.
* `400` means the body or mapping was invalid.
* `401` means the supplied secret or signature was invalid.
* `403` means the source is disabled.
* `404` means the intake token does not resolve, commonly after rotation.
* `429` means the source rate limit was exceeded; retry after the `Retry-After` interval.
Every attempt updates the source card. Use **Send test** after setup or mapping changes, and confirm both a successful delivery and the expected normalized lead before enabling production traffic.
## Rotate credentials
Admins can rotate a source from its card. Rotation issues a new intake URL token and secret immediately; the old values stop resolving. Update the sender, run a live test, and only then resume traffic. Rotation does not change the source ID, delivery history, field mapping, or connected tasks.
## Connect a source to tasks
A source may feed multiple tasks. Connect it from the source card or select it while creating a task. Each matching delivery opens work independently for every connected task.
Team ownership belongs to the task, not the source. Admins and full-scope users may leave a task workspace-wide or choose an allowed team. Team-scoped members must choose one of their teams.
## Hosted and embedded forms
RevDesk hosted forms keep their published URLs:
```text theme={null}
https://app.revdesk.com/f/{formId}
https://app.revdesk.com/api/automation-forms/{formId}
```
Existing embed snippets continue to work. Form IDs, branding, domain allowlists, and submission history are preserved when the form is connected to a lead source. Cross-origin embeds remain subject to their configured domain allowlist, honeypot, CORS, and durable rate limit; the same-host hosted page also uses browser bot protection.
## ADF sources
ADF sources can receive either ADF XML by webhook or ADF email at the address shown on the source card. Both transports resolve the same lead source, apply the saved mapping, log the attempt, and feed the same connected tasks.
Turn a new source delivery into immediate follow-up.
Subscribe your systems to call, message, and task outcomes.
# Team Management
Source: https://docs.revdesk.com/getting-started/features/teams
Set up your team and manage permissions. Control who has access to what.
Add team members, set permissions, and organize your team for efficient call handling.
***
## Adding team members
Navigate to Settings → Team
Enter their email address
Choose their permission level
They'll receive an email to join
***
## Roles and permissions
| Role | What they can do |
| - | - |
| **Owner** | Everything. Billing, settings, team management |
| **Admin** | Most things. Settings, team, but not billing |
| **Manager** | View all calls, manage calendar, limited settings |
| **Member** | View own calls, manage own calendar |
| **Viewer** | Read-only access to calls and transcripts |
***
## Permission details
### Owner
* Full access to everything
* Manage billing and subscription
* Add/remove team members
* Delete account
### Admin
* All settings except billing
* Add/remove members (not Owners)
* Access all calls and data
* Configure AI and integrations
### Manager
* View all team calls
* Manage team calendars
* Assign calls to members
* Run reports
### Member
* View calls assigned to them
* Manage own calendar
* Handle assigned conversations
### Viewer
* Read-only access
* View calls and transcripts
* Cannot make changes
***
## Team structure
Organize your team by:
* **Departments** - Sales, Support, Operations
* **Locations** - If you have multiple offices
* **Services** - By service type offered
This helps with routing calls to the right people.
***
## Calendar per member
Each team member can have their own calendar:
* **Individual availability** - Set their own hours
* **Personal bookings** - Appointments go to their calendar
* **Round-robin participation** - Include/exclude from rotation
See [Round-Robin](/getting-started/features/round-robin) for team distribution.
***
## Notifications
Configure who gets notified for what:
| Event | Who to notify |
| - | - |
| New call | Assigned member |
| Booking made | Calendar owner |
| Missed call | Team managers |
| Transfer request | Available members |
| After-hours call | On-call team |
***
## Audit log
Track who did what:
* **Settings changes** - Who changed what, when
* **Call actions** - Who listened, responded
* **Team changes** - Invites, role changes
Available to Admins and Owners.
***
## Best practices
Give team members the minimum permissions they need. You can always add more.
Name roles and teams clearly. "Northeast Sales Team" beats "Team 3."
As roles change, update permissions. Remove access when team members leave.
***
## Seats
Every plan includes unlimited seats with no per-seat charges. Add as many team members as you need.
Everyone on the team can use the agents, dial, and get coached.
***
Add and configure team members
# Launch your first workflow
Source: https://docs.revdesk.com/getting-started/onboarding
A seven-step path from a new workspace to a tested RevDesk workflow.
# Launch your first workflow
The best first launch is narrow enough to test and important enough to matter. Pick one audience, one outcome, and a clear point where a person should step in.
Add the business name, locations, teammates, working hours, and the people who should receive handoffs.
Decide what completed work means. This might be a routed call, qualified lead, booked appointment,
reactivated customer, or updated opportunity.
Choose where eligible records arrive from, such as Deal Operations, a supported CRM, a form, an approved
list, or an integration.
Configure the phone and messaging channels the workflow may use. Complete any required number
registration, consent, and sender setup before launch.
Set the audience, goal, allowed actions, schedule, maximum follow-up, approval points, and human
escalation path.
Use a test record to verify the opening conversation, reply handling, booking or routing, record update,
opt-out behavior, and handoff context.
RevDesk can recommend a launch shape after the systems, audience, and rules are known. Review the
recommendation, confirm ownership, then start with a controlled audience.
## A launch is ready when
* Every eligible record has one source and one owner.
* The agent has a specific outcome and a defined stopping point.
* Approved channels, working hours, consent rules, and opt-outs are configured.
* A person knows where handoffs arrive and what response time is expected.
* The correct stage, activity, appointment, owner, and next step update in Deal Operations or a verified integration.
* Managers know where to review work, outcomes, and exceptions.
Verify the full customer and teammate experience before launch.
Learn how agents receive work, act, stop, and hand off.
# Quickstart
Source: https://docs.revdesk.com/getting-started/quickstart
Configure and test one complete RevDesk workflow before expanding the audience or channels.
# Build one complete workflow
Start with one trigger, one audience, one outcome, and one handoff path. The goal is to verify that work can enter RevDesk, reach the right person, produce a clear next step, and update the expected record.
Exact setup screens depend on your plan, enabled channels, integrations, and deployment. Use [Launch your
first workflow](/getting-started/onboarding) for the full checklist.
## 1. Create or join a workspace
A workspace is the customer-facing name for your RevDesk organization. Add the business, locations, teammates, working hours, and the people responsible for handoffs.
## 2. Choose a narrow first outcome
Good first outcomes are easy to recognize and verify:
* Route an inbound call to the right person.
* Respond to a new lead and book an appointment.
* Re-engage an approved audience and hand off qualified interest.
* Follow up after a visit and record the response.
* Create or update the supported opportunity and next step.
Avoid combining several audiences, departments, and outcomes in the first launch.
## 3. Connect the required systems and channels
Connect only what the workflow needs. This may include:
* A RevDesk or ported phone number
* A supported messaging sender
* Deal Operations or a verified CRM integration
* A calendar used for availability and booking
* A form, approved list, or integration that supplies eligible records
Set up a number and review registration requirements.
Review supported CRM, calendar, messaging, and automation connections.
Evaluate one controlled workflow with test limits before production launch.
Review the units, balances, and billing behavior that apply to your enabled channels.
## 4. Define the agent task
Write down:
* **Audience:** Which records are eligible and which must be excluded?
* **Outcome:** What does completed work look like?
* **Channels:** Which approved channels may the agent use?
* **Schedule:** When may work run and how long may follow-up continue?
* **Actions:** What may RevDesk say, create, update, book, or route?
* **Approval:** Which actions require review?
* **Handoff:** When should a person take over, and who owns the response?
* **Stopping rules:** Which reply, outcome, opt-out, or exception ends the task?
## 5. Test from trigger to record update
Use a test record and verify the whole path:
1. The record enters the correct workspace and location.
2. The agent receives the right context and starts within the configured schedule.
3. The opening conversation and reply handling match the approved playbook.
4. Booking, routing, approval, opt-out, and human handoff work as expected.
5. Deal Operations or the verified integration records the correct activity, owner, stage, appointment, outcome, and next step.
6. Managers can find the work and understand what needs attention.
Launch to a controlled audience only after the complete path works. Expand one variable at a time, such as
audience size, channel, location, or workflow.
## Next steps
Verify customer, agent, teammate, and record behavior.
Understand included voice minutes, native channel units, and the usage balance.
Refine instructions after the base workflow is reliable.
Build a custom integration or event consumer.
# Phone Numbers
Source: https://docs.revdesk.com/getting-started/setup/phone-numbers
Get a new phone number or forward your existing one. Your AI needs a number to answer.
Your AI needs a phone number to answer. You can get a new RevDesk number instantly or forward calls from your existing business line.
***
## Two options
**Instant setup**
Get a dedicated RevDesk number with your preferred area code. Live immediately.
Best for: Testing, new businesses, separate AI line
**Keep your number**
Forward calls from your current business line to RevDesk. Customers call the same number.
Best for: Established businesses, keeping existing number
***
## Option A: Get a new RevDesk number
### How it works
1. Choose your area code
2. Pick from available numbers
3. Done. Number is live immediately
### Choosing an area code
Pick an area code that makes sense for your business:
* **Local area code.** Customers see a familiar number
* **Toll-free (800/888).** Professional, nationwide feel
* **Specific market.** Target a particular geographic area
### Pricing
[Contact sales](https://cal.com/revdesk/30min) for local and toll-free number pricing.
Most businesses start with one number. You can add more anytime.
***
## Option B: Forward your existing number
### How it works
Keep your current business phone number. Forward calls to your RevDesk number when you want AI to answer.
This is the destination for forwarded calls
Configure your current provider to forward calls
Call your business line and verify AI answers
### Forwarding options
All calls go to AI. You never ring.
Best for: Full AI takeover, after-hours coverage
Calls forward to AI when you're on another call.
Best for: Overflow handling
Calls forward to AI after X rings.
Best for: Backup when you can't answer
Forward during specific hours (nights, weekends).
Best for: After-hours coverage only
### Setting up forwarding by carrier
1. Dial `*21*` followed by your RevDesk number, then `#`
2. Example: `*21*555-123-4567#`
3. Press Call
4. To disable: Dial `#21#`
1. Dial `*72` 2. Enter your RevDesk number 3. Press Call and wait for confirmation 4. To disable: Dial `*73`
1. Dial `**21*` followed by your RevDesk number, then `#` 2. Example: `**21*555-123-4567#` 3. Press Call 4.
To disable: Dial `##21#`
Contact your phone provider to set up call forwarding. Most business lines support forwarding to any number.
Log into your VoIP dashboard and set up forwarding rules. Most VoIP providers have flexible forwarding options.
***
## Option C: Port your number
Want RevDesk to fully own your existing number? You can port it over.
### How porting works
1. Submit a port request with your current carrier info
2. We handle the transfer (usually 1-2 weeks)
3. Your number now lives on RevDesk
4. All calls come directly to AI
### When to port
Port if you want to:
* Eliminate your current phone bill
* Have full control in RevDesk
* Stop managing forwarding
### When not to port
Keep forwarding if you:
* Want to keep your current carrier
* Need to receive calls on a physical phone sometimes
* Are still testing RevDesk
***
## Multiple phone numbers
Some businesses need multiple numbers:
| Use case | Setup |
| - | - |
| **Different departments** | Sales line → AI qualifies leads
Support line → AI handles common questions |
| **Different locations** | Each location has its own local number |
| **Marketing campaigns** | Track which ads drive calls |
| **Languages** | Spanish line → Spanish-speaking AI |
| **Outbound capacity** | More numbers = more calls per day |
Add as many numbers as you need. Each can have different AI configurations.
***
## Outbound call capacity
If you're running outbound campaigns, two things control your throughput: the number of phone numbers you own (each one is capped at 75 outbound calls per day to protect carrier reputation) and the number of dedicated concurrent lines you have (each runs one call at a time; contact sales for pricing). Local area codes get materially higher answer rates than toll-free numbers, so most outbound campaigns are best run from a pool of local numbers.
Full capacity math, budget-vs-goal tables, and recommended starter setups.
***
## Branded caller ID
Without registration, outbound calls show as a 10-digit number, or worse, get labeled "Spam Likely". With Branded Calling, the receiving phone shows your business name. Recipients can recognize the call.
| Before registration | After registration |
| - | - |
| `(555) 123-4567` or `Spam Likely` | `Smith HVAC` |
### What it does
* **Displays your business name** on the receiving handset across the major US carriers.
* **Makes the call recognizable.** Recipients can see who is calling before they answer.
* **Reduces spam flagging** by giving carriers a verified identity to associate with your numbers.
* **Includes STIR/SHAKEN attestation** so your calls are cryptographically signed as legitimate.
### Registration process
Legal business name, address, EIN or Tax ID.
Confirm you're authorized to represent the business.
RevDesk submits to AT\&T, Verizon, T-Mobile, US Cellular, and most regional carriers.
Usually 5 to 10 business days.
Your name starts displaying on outbound calls automatically.
### What displays
| Option | Example |
| - | - |
| Business name | "Smith HVAC" |
| Business + location | "Smith HVAC Dallas" |
| Business + department | "Smith HVAC Sales" |
Keep it short. 15 characters or fewer renders best.
### Pricing
Branded Calling is a RevDesk add-on. [Contact sales](https://cal.com/revdesk/30min) for pricing.
### Maintaining reputation
Once registered, the reputation of your numbers is something you have to protect. Call during reasonable hours, honor opt-outs immediately, keep the message relevant, and identify yourself clearly. Spam reports and block rates show up in your dashboard so you can react before reputation deteriorates.
### Troubleshooting
Older phones and a few smaller carriers don't display Branded Calling. Major carriers on current devices
will.
Reputation takes time to build. Reduce call volume temporarily and focus on quality calls.
Usually a mismatch in business info. Verify the EIN and legal name match official records exactly.
## Voicemail
If you enable voicemail fallback for outbound:
* Inbound calls always reach AI directly (no voicemail).
* If the AI can't complete an outbound call, it can leave a voicemail per your script.
***
## Testing your number
After setup, test it:
* Call your RevDesk number from your cell phone
* Verify AI answers with your business info
* Try booking an appointment
* Check that notifications come through
* Review the transcript in your dashboard
***
## FAQ
We don't recommend it. Get a dedicated business number to keep work and personal separate. [Contact sales](https://cal.com/revdesk/30min) for number pricing.
Use conditional forwarding. Forward to AI after X rings, during certain hours, or when you're busy. You
control when AI picks up.
Yes. AI can transfer calls to you or any team member. Set up transfer rules for emergencies, VIP customers,
or complex requests.
No. One number can handle all your automations (Answer & Route, reminders, callbacks). The AI knows which context to use based on who's calling and why.
***
Set up your phone number in 60 seconds.
# Testing Your AI
Source: https://docs.revdesk.com/getting-started/setup/testing
Test your AI before going live. Make sure it handles calls the way you expect.
Before sending real customer calls to your AI, test it thoroughly. This guide walks you through how to verify everything works correctly.
***
## Quick test
The fastest way to test:
1. **Call your RevDesk number** from your personal phone
2. **Have a conversation** as if you were a customer
3. **Review the transcript** in your dashboard
That's it. You can test anytime by simply calling.
***
## What to test
### Basic functionality
* AI answers the phone
* AI introduces itself correctly
* AI knows your business name
* AI knows your business hours
* AI can book an appointment
* You receive notifications
* Transcript appears in dashboard
### Common scenarios
Test these real-world situations:
| Scenario | What to check |
| - | - |
| **Simple booking** | Customer wants to schedule. AI checks calendar and books. |
| **Information request** | Customer asks about services/pricing. AI answers correctly. |
| **Busy time request** | Customer requests a time you're unavailable. AI offers alternatives. |
| **Unknown question** | Customer asks something not in your knowledge base. AI handles gracefully. |
| **Frustrated customer** | Customer sounds upset. AI responds with empathy. |
| **Reschedule request** | Customer wants to change an existing appointment. |
| **Transfer request** | Customer asks to speak to a human. AI transfers correctly. |
***
## Test scripts
Use these scripts to verify specific behaviors:
### Script 1: Basic booking
> **You**: "Hi, I'd like to schedule a service appointment."
>
> **Check**: Does AI ask what service? Offer available times? Book correctly?
### Script 2: Pricing question
> **You**: "How much does \[your service] cost?"
>
> **Check**: Does AI give the correct price range from your knowledge base?
### Script 3: Unavailable time
> **You**: "I need an appointment tomorrow at 3am."
>
> **Check**: Does AI explain you're not available then and offer alternatives?
### Script 4: Unknown question
> **You**: "Do you offer a service you've never mentioned?"
>
> **Check**: Does AI acknowledge it doesn't know and offer to take a message?
### Script 5: Upset customer
> **You** (frustrated tone): "I've been waiting for hours and no one showed up!"
>
> **Check**: Does AI apologize, show empathy, and try to help resolve?
### Script 6: Transfer request
> **You**: "I need to speak to a manager right now."
>
> **Check**: Does AI transfer or take a message according to your settings?
***
## Reviewing transcripts
After each test call, review the transcript:
### Where to find transcripts
1. Go to your Dashboard
2. Click on "Calls" or "Activity"
3. Find your test call
4. Click to view full transcript
### What to look for
* Natural conversation flow
* Correct information provided
* Appropriate tone
* Successful booking (if applicable)
* Proper handling of unknowns
* Awkward or robotic responses
* Incorrect information
* Wrong tone for situation
* Failed to book when it should have
* Got stuck in a loop
***
## Listening to recordings
Beyond transcripts, listen to the actual audio:
1. Find the call in your dashboard
2. Click the play button on the recording
3. Listen for:
* Natural-sounding voice
* Appropriate pacing
* Correct pronunciation
* No awkward pauses
***
## Testing different conditions
### Time-based testing
Test how AI behaves at different times:
* **During business hours**: Should offer same-day appointments
* **After hours**: Should acknowledge you're closed, offer next day
* **Holidays**: Should handle according to your holiday settings
You can temporarily adjust your availability settings to test different scenarios without waiting for actual after-hours.
### Calendar testing
Verify calendar integration:
1. **Add a busy time** to your calendar
2. **Call and request** that exact time
3. **Verify** AI doesn't offer it
Then:
1. **Book an appointment** via AI
2. **Check your calendar.** does it appear?
3. **Verify details.** customer name, service, notes
### Language testing
If you have multilingual mode enabled:
1. **Call and speak** in another language
2. **Verify** AI detects and responds in that language
3. **Check transcript.** should show the conversation in that language
***
## Common issues and fixes
Go to Settings → Business Info → Hours and update your hours. Changes take effect immediately.
Add the information to your Knowledge Base. The more specific, the better.
Try a different voice in Settings → Voice. Adjust speaking pace if needed.
Check that your calendar is properly synced. Verify the correct calendar is selected.
Check your transfer settings. Make sure you've set up which requests trigger transfers.
Wait a few minutes. Transcripts can take 1-2 minutes to generate. If still missing, contact support.
***
## Pre-launch checklist
Before sending real customers to your AI:
* Made at least 5 test calls
* Tested booking flow end-to-end
* Tested common questions
* Tested edge cases (unknown questions, upset caller)
* Reviewed transcripts for accuracy
* Listened to at least 2 recordings
* Verified calendar sync works
* Verified notifications work
* Had someone else test (fresh perspective)
***
## Ongoing testing
Testing isn't just for launch. Regularly:
1. **Review transcripts** of real calls
2. **Identify patterns.** what questions come up often?
3. **Fill knowledge gaps.** add answers for new questions
4. **Make test calls** after changes to verify they work
***
## Getting help
If something isn't working as expected:
Common questions answered
[support@revdesk.com](mailto:support@revdesk.com)
Available during business hours
***
Call your number and verify everything works.
# Voice & Personality
Source: https://docs.revdesk.com/getting-started/setup/voice-personality
Choose how your AI sounds and communicates. Pick a voice, set the tone, and make it yours.
Your AI should sound like it belongs to your business. Choose a voice, set the communication style, and make it feel like a natural extension of your team.
***
## Choosing a voice
RevDesk offers multiple AI voices. Each has different characteristics:
### Voice options
| Voice | Description | Best for |
| - | - | - |
| **Professional** | Clear, confident, business-like | Law firms, financial services, B2B |
| **Friendly** | Warm, approachable, conversational | Home services, healthcare, retail |
| **Energetic** | Upbeat, enthusiastic, dynamic | Sales, fitness, youth-oriented |
| **Calm** | Soothing, patient, reassuring | Healthcare, therapy, senior services |
### Previewing voices
Before choosing, preview each voice with sample phrases:
1. Go to Settings → Voice
2. Click "Preview" on each option
3. Hear how it sounds with your business name
4. Select the one that fits
***
## Voice speed and tone
Beyond the voice itself, adjust how it speaks:
### Speaking pace
| Setting | Effect |
| - | - |
| **Slower** | More deliberate, easier to understand |
| **Normal** | Natural conversational pace |
| **Faster** | More energetic, efficient |
Slower pace works well for technical explanations or older audiences. Faster pace suits energetic brands and younger demographics.
### Tone adjustments
Fine-tune the emotional quality:
* **Warmth**: How personable and caring
* **Formality**: How professional vs. casual
* **Energy**: How enthusiastic vs. measured
***
## Communication style
### Greeting
How does AI answer the phone?
**Formal:**
> "Good afternoon, thank you for calling Johnson Law Firm. How may I assist you today?"
**Friendly:**
> "Hi there! Thanks for calling Johnson Plumbing. What can I help you with?"
**Efficient:**
> "Johnson HVAC, how can I help you?"
Customize your greeting in Settings → Scripts → Greeting.
### Conversation style
Set general guidelines for how AI communicates:
Get to the point. Don't ramble.
Explain fully. Don't rush.
Acknowledge feelings. Show understanding.
Focus on solving the problem quickly.
***
## Language support
RevDesk AI supports **42 languages** plus a multilingual auto-detect mode.
### Setting the primary language
Your AI's default language. It will use this unless the caller speaks something else.
**Major languages:**
* English
* Spanish
* French
* German
* Portuguese
* Italian
* Chinese
* Japanese
* Korean
* Hindi
* Arabic
* Russian
**European languages:**
* Dutch
* Polish
* Swedish
* Turkish
* Danish
* Norwegian
* Finnish
* Greek
* Czech
* Romanian
* Hungarian
* Slovak
* Bulgarian
* Ukrainian
* Croatian
* Hebrew
**Asian & Southeast Asian languages:**
* Thai
* Vietnamese
* Indonesian
* Malay
* Tagalog
**South Asian languages:**
* Bengali
* Tamil
* Telugu
* Marathi
* Gujarati
* Kannada
* Malayalam
* Punjabi
**Other languages:**
* Georgian
### Multilingual mode
Enable automatic language detection:
1. Caller speaks in Spanish
2. AI detects the language
3. AI responds in Spanish
4. Conversation continues in Spanish
No prompting needed. It just works.
Multilingual auto-detect supports: English, Spanish, French, German, Portuguese, Japanese, Italian, Dutch, Hindi, and Russian.
### Language-specific greetings
Set different greetings for different languages:
**English:**
> "Thanks for calling Johnson Services. How can I help?"
**Spanish:**
> "Gracias por llamar a Johnson Services. ¿Cómo puedo ayudarle?"
***
## Personality traits
Give your AI personality guidelines:
### Examples
**For a law firm:**
* Be professional and authoritative
* Use precise language
* Never make promises about case outcomes
* Express empathy for difficult situations
**For a plumbing company:**
* Be friendly and down-to-earth
* Use simple, clear language
* Express urgency for emergency calls
* Be reassuring about fixing the problem
**For a medical practice:**
* Be warm and compassionate
* Use calm, reassuring tone
* Be patient with questions
* Never give medical advice
### Setting traits
Go to Settings → Personality and add traits like:
* "Be friendly but professional"
* "Use the customer's name when possible"
* "If someone seems frustrated, be extra patient"
* "Always end with 'Is there anything else I can help with?'"
***
## What AI calls itself
### Introduction
How does AI introduce itself?
**As an AI:**
> "Hi, this is the AI assistant for Johnson Plumbing. How can I help?"
**As a team member:**
> "Hi, thanks for calling Johnson Plumbing. How can I help you today?"
When directly asked if they're speaking to a human, AI will always be honest. It won't pretend to be human.
### Name
Give your AI a name if you want:
* "This is Alex from Johnson Plumbing"
* "This is the scheduling assistant for Johnson Plumbing"
* No name: "Thanks for calling Johnson Plumbing"
***
## Outbound prompt
When your AI makes outbound calls (campaigns, workflows, follow-ups), it uses a separate greeting and set of instructions from your inbound configuration.
### Configuring outbound defaults
1. Go to your phone number's **Voice** tab.
2. Expand the **Outbound Prompt** section.
3. Set your **Outbound Greeting**, the first thing the AI says when calling someone.
4. Set your **Outbound Instructions**, the way the AI should behave during outbound calls.
Outbound calls have different dynamics than inbound. The person didn't initiate the call, so your greeting should identify who's calling and confirm it's a good time to talk.
### Example outbound greeting
> "Hi there! This is an AI assistant calling on behalf of Johnson Plumbing. Is now a good time to talk?"
### Example outbound instructions
* State the purpose of the call clearly and concisely.
* If they're busy, offer to call back at a better time.
* If they're not interested, be gracious and end the call.
* If you don't have the answer, offer to find out and follow up.
Individual campaigns and workflows can override these defaults with their own scripts. The outbound prompt serves as the fallback when no specific script is provided.
***
## Testing your settings
After configuring voice and personality:
1. **Call your number** and have a conversation
2. **Listen to how it sounds.** Does it match your brand?
3. **Try different scenarios.** Friendly caller, frustrated caller, quick question
4. **Review transcripts.** Does the text read the way you want?
5. **Adjust and repeat.** Fine-tune until it feels right
***
## Best practices
Your AI should sound like an extension of your existing brand voice.
Simulated tests only go so far. See how it handles real conversations.
Ask customers what they thought. Their perception matters.
Your first setup won't be perfect. Adjust based on what you learn.
***
## FAQ
Custom voice cloning is available by agreement. Contact sales.
Yes. Change voice settings anytime. The new voice takes effect immediately for all future calls.
AI uses native-sounding voices for each language. The English voice and Spanish voice are both optimized for their respective languages.
Yes. Answer & Route can use one voice while reminder calls use another. Configure per-automation in Settings.
***
Choose your voice and personality settings.
# Usage and billing
Source: https://docs.revdesk.com/getting-started/usage-and-billing
How included voice minutes, channel usage, and the RevDesk usage balance work.
# Usage and billing
RevDesk keeps channel usage understandable by measuring each channel in its natural unit, then applying the rate in your plan or quote to one dollar-denominated usage balance.
## What your plan includes
RevDesk has two standard software plans. Growth provides the complete software foundation. Business adds advanced coordination and the applicable vertical workflows. [Contact sales](https://cal.com/revdesk/30min) for plan pricing and included voice minutes. Custom and Managed scopes are written into the agreement. Included voice minutes are used before paid voice usage draws from the usage balance.
Deal Scanner is included with RevDesk Business for dealerships.
RevDesk Add-ons is the single optional-add-on category. Its separate prices cover Phone Numbers, A2P Registration, Branded Calling, and Priority Slack Support; none is another software plan.
## How usage is measured
| Activity | Native unit |
| - | - |
| AI voice | Connected minute |
| SMS and supported messaging | Message segment or channel event defined in your quote |
| Email | Sent email |
| Managed outreach | Approved audience, capacity, or outcome defined in the engagement |
| Deal Operations | Completed-deal definition written into the quote, when applicable |
RevDesk does not combine unlike channel activity into an abstract message-credit score. The dashboard should show the native activity, the applicable rate, and the dollar amount deducted from the usage balance.
Channel availability, carrier costs, registration requirements, and billing units can vary by deployment.
Your signed quote is the source of truth for enabled channels and rates.
## Staying in control
* Review usage by channel, workflow, location, and period.
* Add funds to the usage balance before planned high-volume work.
* Confirm the rate card and any included units before launch.
* Set audience, schedule, capacity, and stopping rules for outbound work.
* Contact RevDesk before changing a workflow in a way that materially changes volume.
See how RevDesk scopes plans, locations, usage, transactions, and managed work.
# Answer and Route Inbound Calls
Source: https://docs.revdesk.com/guides/workflows/answer-and-route-inbound
Cover inbound calls, resolve approved requests, book appointments, and route exceptions with context.
# Give every inbound call a controlled path
Use this workflow when a RevDesk number or routed business number should answer routine inbound calls. The
agent identifies the reason for the call, handles approved requests, and books, transfers, or records a
follow-up for the responsible person.
## Prerequisites
* An active RevDesk or ported number with the required registration completed
* Business hours, after-hours behavior, and emergency boundaries
* A knowledge base for common questions and prohibited answers
* Transfer destinations, booking calendars, and named follow-up owners
## Trigger and input
The trigger is an inbound call to the configured number. Caller identity, number configuration, prior
conversation context, and the dialed line can help select the right instructions, but the caller should still
be allowed to state the reason for the call.
## Controls
* List which requests may be completed without a person.
* Define warm-transfer destinations and what happens when nobody answers.
* Separate urgent escalation from routine after-hours intake.
* Require confirmation before creating or changing an appointment or important record.
* State what the agent must not guess, promise, diagnose, quote, or authorize.
## Launch
1. Configure one number and one call purpose.
2. Add the minimum business knowledge and transfer destinations.
3. Connect the relevant calendar if the agent may book.
4. Test during business hours, after hours, with unavailable destinations, and with an unsupported request.
5. Review recordings, transcripts, routing results, and created records before moving production traffic.
## Verify
Call from numbers with and without an existing customer record. Confirm the greeting, disclosure, request
handling, transfer context, fallback behavior, booking, and final inbox timeline. Verify that an urgent or
prohibited request reaches the intended human path.
## Measure
Track answered calls, containment, successful transfers, bookings, abandoned calls, after-hours follow-ups,
and routing corrections. Review outcome quality alongside call volume; a short call is not automatically a
successful one.
## Troubleshooting
* **Calls reach the wrong workflow:** review the dialed number and routing assignment.
* **Transfers fail:** verify the destination, schedule, timeout, and fallback owner.
* **Answers are too broad:** reduce the knowledge scope and add explicit escalation boundaries.
* **Bookings conflict:** confirm calendar connection, host rules, buffers, and timezone.
See [Phone Numbers](/getting-started/setup/phone-numbers) and
[Voice & Personality](/getting-started/setup/voice-personality).
# Build and Enrich an Audience
Source: https://docs.revdesk.com/guides/workflows/build-and-enrich-an-audience
Prepare eligible customer records and add the information a RevDesk workflow needs before outreach begins.
# Prepare records for the next action
Use this workflow when RevDesk needs a controlled list of people or organizations before calling, messaging,
or assigning work. The result is not a large database; it is an eligible, deduplicated audience with enough
context to take the next approved action.
## Prerequisites
* An approved source such as Deal Operations, a verified CRM integration, or an imported list
* A named owner for field definitions and duplicate handling
* Documented eligibility, exclusion, consent, and suppression rules
* A defined downstream workflow and the fields it actually needs
## Trigger and input
Choose one source and one trigger for the first launch. Examples include a saved CRM segment, a scheduled
approved-list import, or a new record from a connected form. Record the source, import time, and applicable
consent context so operators can explain why a person entered the audience.
## Controls
* Define required fields separately from optional enrichment.
* Match records using stable identifiers before creating a new contact or company.
* Exclude opt-outs, blocked records, existing customers when inappropriate, and records outside the approved
geography or business criteria.
* Do not infer sensitive data or use a field merely because it is available.
* Decide which system owns each field and where corrections must be made.
## Launch
1. Build a small segment containing known matches, duplicates, exclusions, and incomplete records.
2. Map only the fields needed by the downstream task.
3. Preview which records will be created, updated, skipped, or flagged for review.
4. Run the audience build without starting outreach.
5. Review the result with the business owner, then enable the downstream workflow separately.
## Verify
Confirm that eligible records appear once, excluded records do not enter the task, source attribution is
preserved, and updates land in the agreed system. Open several records from each result category instead of
checking only totals.
## Measure
Track eligible records, duplicates prevented, excluded records, records requiring review, and the share with
all downstream-required fields. Treat contact or booking outcomes as measures of the later workflow, not of
enrichment itself.
## Troubleshooting
* **Too many duplicates:** tighten matching rules and resolve the owning-system identifiers.
* **Unexpected exclusions:** inspect the source segment, consent state, and suppression reason.
* **Fields revert later:** two systems probably claim ownership; choose one write direction.
* **Outreach starts too early:** separate audience preparation from task activation and require review.
Next, use [Run outbound outreach](/guides/workflows/run-outbound-outreach) or
[Qualify and book new leads](/guides/workflows/qualify-and-book-new-leads).
# Follow Up, Reactivate, and Retain Customers
Source: https://docs.revdesk.com/guides/workflows/follow-up-and-retention
Recover appointments, re-engage eligible customers, collect feedback, and record the next step.
# Continue the relationship from the existing record
Use this workflow after an inquiry, booking, visit, purchase, or period of inactivity. RevDesk follows the
approved schedule, helps the customer take a clear next step, and records the result for the person or system
that owns the relationship.
## Prerequisites
* A reliable customer, appointment, or service event from Deal Operations or a verified integration
* Eligibility and suppression rules for the follow-up purpose
* A booking, feedback, review, service, or reactivation outcome
* An owner for complaints, sensitive replies, and exceptions
## Trigger and input
Choose one lifecycle event, such as a new appointment, missed appointment, completed service, renewal window,
or approved inactive-customer segment. Include the original context and current status so the customer is not
asked to repeat information or contacted after the need has already been resolved.
## Controls
* Use a purpose-specific schedule and attempt limit.
* Keep transactional reminders separate from promotional outreach.
* Define which changes may happen automatically and which require confirmation or review.
* Route dissatisfaction, clinical or legal questions, disputes, and sensitive exceptions to a person.
* Stop on completion, opt-out, invalid contact data, human takeover, or a changed source record.
## Launch
1. Select one lifecycle trigger and a small set of recent records.
2. Write the desired next step and the conditions that end the task.
3. Connect the calendar, feedback destination, or record system needed to complete it.
4. Test completed, rescheduled, dissatisfied, no-response, and opt-out scenarios.
5. Review the first production outcomes before increasing the time window or audience.
## Verify
Confirm the customer receives the correct context, any booking or status change reaches the owning system,
negative feedback is routed privately, and completed or opted-out customers receive no later actions.
## Measure
Track confirmations, recovered appointments, reactivations, completed follow-ups, human escalations,
opt-outs, and incorrect-state reports. Measure reviews or referrals only after confirming that dissatisfied
responses follow the intended service-recovery path.
## Troubleshooting
* **A completed customer is contacted again:** inspect source synchronization and stopping events.
* **Reminders have stale details:** confirm which calendar or record owns time, location, and status.
* **Negative feedback is sent publicly:** require sentiment or response review before sharing a review link.
* **A person cannot see the history:** verify inbox threading and the integration write-back fields.
See [Inbox](/getting-started/features/inbox) and [Tasks](/concepts/tasks) for the operational record.
# Workflow Guides
Source: https://docs.revdesk.com/guides/workflows/index
Launch practical RevDesk workflows with clear inputs, controls, handoffs, and verification steps.
# Start with the outcome
A reliable workflow has one eligible audience, one trigger, one outcome, and one human handoff path. Choose
the guide closest to the work you want RevDesk to run, then narrow it to your business rules.
Prepare eligible records and fill only the fields the next step needs.
Respond to a new inquiry, qualify it, and book or hand it off.
Cover inbound calls, resolve routine requests, and route exceptions.
Work an approved list with controlled pacing, review, and stopping rules.
Recover appointments, re-engage customers, and record the next step.
## The shared launch pattern
Every guide uses the same operating model:
1. Confirm the eligible audience and source of truth.
2. Define the outcome, allowed actions, schedule, and stopping rules.
3. Connect the minimum channels and systems required.
4. Test with controlled records and real handoff owners.
5. Review the resulting conversation, task, consent, and record updates.
6. Expand only after the complete path is reliable.
Availability varies by product, account, country, channel, integration, and deployment. Confirm the
capabilities enabled for your workspace before promising a workflow to customers.
For industry examples and customer stories, see [Solutions](https://www.revdesk.com/solutions) and
[Case Studies](https://www.revdesk.com/case-studies).
# Qualify and Book New Leads
Source: https://docs.revdesk.com/guides/workflows/qualify-and-book-new-leads
Respond to new inquiries, apply your qualification rules, and book or route the next step.
# Turn one new inquiry into one clear next step
Use this workflow for form fills, inbound inquiries, or other high-intent records that need a prompt response.
RevDesk contacts the lead through the approved channels, gathers the required information, and books, routes,
or closes the task according to your rules.
## Prerequisites
* A trusted trigger that supplies contact information and consent context
* Written qualification criteria and disqualifying conditions
* A connected calendar or named human queue for the next step
* Working hours, retry limits, channel permissions, and an escalation owner
## Trigger and input
Start with one lead source. Require the fields needed to identify the person, determine the correct workflow,
and route the result. If the source cannot provide required consent or ownership information, hold the record
for review instead of assuming it.
## Controls
* Set the allowed contact window and maximum attempts.
* Define which qualification answers are required before booking.
* Decide when a person must approve, answer, or take over.
* Keep calendar routing rules explicit: eligible hosts, location, appointment type, duration, and buffers.
* Stop on opt-out, completed booking, disqualification, invalid contact information, or human takeover.
## Launch
1. Connect the lead source and map a small set of required fields.
2. Configure the agent instructions and knowledge needed for this inquiry type.
3. Connect the destination calendar or handoff queue.
4. Test qualified, unqualified, unavailable, opt-out, and no-response scenarios.
5. Launch to one controlled source before adding more forms, locations, or channels.
## Verify
Inspect the first conversation, qualification answers, booking or handoff, consent events, and final record.
Confirm that the appointment appears on the correct live calendar and that a human can see why the lead was
booked, routed, or closed.
## Measure
Track time to first approved action, contact rate, qualified rate, booking rate, human-handoff rate, opt-outs,
and incorrect-routing reports. Compare each metric by lead source instead of combining unlike audiences.
## A complete run
This is the shape of one verified run of this workflow, taken from the dealership deployments in the
[customer stories](https://www.revdesk.com/case-studies). Names are illustrative. What RevDesk completes
and what stays with a person are separated on purpose.
| Step | What happens |
| - | - |
| **Starting request** | A web inquiry about a specific unit arrives from the store's ADF lead feed on a Saturday afternoon. The form carries a name, mobile number, the unit, and the consent language the store requires. |
| **Available context** | Lead source, rooftop, unit and its current inventory status, the store's knowledge base (hours, financing basics, trade policy), calendar availability for the assigned salespeople, and any prior conversation on this contact's record. Guardrails: call and text only, quiet hours local to the customer, three attempts, no email. |
| **Actions taken** | The agent calls within the approved window. No answer. The policy layer picks a text next because the day-two call is the wrong move once a text is likelier to be answered. The customer replies asking about the price. The agent answers from inventory, confirms towable versus motorized and whether there is a trade, checks the salesperson's calendar, and offers two slots. The customer takes Thursday at 2:00pm. |
| **Finished deliverable** | A booked walk-through on the right salesperson's calendar, a confirmation text to the customer, and a structured summary of what the customer asked and answered. |
| **System updates** | Contact created or updated; the deal created from intake and moved to Appointment set; the call, text thread, transcript, summary, and outcome tags on the record; the appointment written to the connected calendar. Where a supported CRM is connected, the activity, appointment, and next action are written there. |
| **Human approval** | None was required to book, because booking was an allowed action. Had the customer asked for a trade value or an out-of-policy discount, the agent would have requested a callback for a person and the deal would have waited. Per-touch review can be turned on so a person approves each drafted message before it sends. |
Remaining with the customer or another system: the visit itself, the trade appraisal, and every desking,
funding, titling, and registration step. RevDesk records the next action; it does not perform those steps.
## Troubleshooting
* **The wrong host is booked:** review calendar ownership, routing criteria, and availability settings.
* **Leads repeat information:** confirm that the trigger fields are mapped into the task context.
* **A lead is contacted outside policy:** inspect timezone, working hours, consent, and source configuration.
* **Qualification is inconsistent:** replace broad prompts with explicit questions and stopping conditions.
See [Real-time calendar booking](/concepts/your-calendar) and
[Testing](/getting-started/setup/testing) for supporting setup.
# Run Outbound Outreach
Source: https://docs.revdesk.com/guides/workflows/run-outbound-outreach
Work an approved audience with controlled calling and messaging, review, pacing, and stopping rules.
# Run approved outreach without losing control
Use this workflow for prospecting, follow-up, or representative-assisted calling from an audience your team is
authorized to contact. RevDesk works each task within the configured schedule and hands interested or
exceptional conversations to a person with context.
## Prerequisites
* A reviewed audience with source, consent, suppression, and ownership information
* Registered sending numbers and approved messaging identities
* A clear offer, qualification path, and human owner
* Calling hours, pacing, attempt limits, and channel-specific compliance rules
## Trigger and input
Start from one reviewed audience or saved source. Each task should include the record identity, reason for
contact, permitted channels, relevant context, and the outcome being pursued. Do not use enrichment or list
membership as proof of consent.
## Controls
* Apply DNC, opt-out, quiet-hours, and frequency rules before every action.
* Set concurrency and pacing within the capacity of the people receiving handoffs.
* Require review for messages, audiences, or actions that need approval.
* Define when voicemail, no answer, invalid contact data, or a reply changes the next step.
* Stop immediately on opt-out, completed outcome, human takeover, or an excluded condition.
## Launch
1. Review a small sample of the audience and its consent evidence.
2. Configure one task goal and one handoff destination.
3. Test the opening, qualification, objections, voicemail, opt-out, and warm handoff.
4. Launch with conservative capacity while an operator watches the first results.
5. Expand volume only after handoffs, records, suppression, and retry behavior are correct.
## Verify
Inspect the outreach audit, task timeline, conversation, outcome, and CRM or Deal Operations update. Confirm
that replies suppress later actions, that a phone opt-out stops calls, SMS, and iMessage to that number, and
that email and WhatsApp opt-outs apply to that channel.
## Measure
Track attempted, reached, meaningful conversations, qualified handoffs, completed outcomes, opt-outs,
complaints, and records requiring correction. Segment results by audience source and workflow version.
## Troubleshooting
* **Operators receive too many handoffs:** reduce concurrency or narrow qualification.
* **Contacts receive repeated attempts:** inspect stopping rules, duplicate records, and task state.
* **Delivery or pickup falls:** review number registration, reputation, content, and audience quality.
* **A channel is unavailable:** confirm account, country, sender, recipient capability, and consent support.
See [Call Capacity Planning](/getting-started/features/call-capacity) and
[Compliance & Regulations](/getting-started/features/compliance).
# RevDesk Documentation
Source: https://docs.revdesk.com/index
Set up RevDesk, run customer workflows, build integrations, and use the public API.
# Find the guide for your job
RevDesk connects customer conversations to the work behind them. Start with the path that matches what you
need to accomplish.
Configure a workspace, launch a controlled workflow, and learn how your team operates it.
Connect a system, consume events, and plan reliable data ownership and recovery.
Review security, privacy, legal, and engineering-control documentation.
Authenticate, explore endpoints, and build with the SDK, MCP server, or REST API.
## New to RevDesk?
Start with one audience, one trigger, one measurable result, and one human handoff path.
Add the required phone number, messaging sender, calendar, and record system.
Verify the customer experience, agent behavior, handoff, opt-out handling, and final record update.
Increase one variable at a time, such as audience size, channel, location, or workflow.
Build and verify one complete workflow.
Follow practical launch guides for inbound, outbound, lead conversion, and retention work.
## Need commercial information?
Product documentation explains behavior and setup. Current plans, industry solutions, customer stories, and
partner programs live on the RevDesk website.
Review current plans and offers.
Map RevDesk to your locations, systems, and first workflow.
# Google Sheets
Source: https://docs.revdesk.com/integrations/automation/google-sheets
Auto-sync qualified leads from AI calls to a Google Sheet. Great for shared visibility, ad-hoc reporting, or feeding another tool.
Pipe qualified leads from RevDesk into a Google Sheet in real time. Every time the AI qualifies a lead on a call, a new row is appended with the contact details, qualification data, and links to the recording and transcript.
***
## What it does
* **Append rows on qualification.** Every qualified lead becomes a row in the sheet you pick.
* **One sheet per integration.** Use Zapier or a formula if you need to fan out to multiple destinations.
* **Reverse-chronological order.** Newest rows at the top so you can glance at the latest leads without scrolling.
***
## Setup
Go to **Settings → Integrations → Google Sheets** and click **Connect**. Sign in to the Google account that owns the sheet you want to write to.
Choose an existing spreadsheet or let RevDesk create one. If you let RevDesk create it, the header row is pre-filled with the standard columns.
By default RevDesk writes a fixed set of columns (see below). If your sheet has a custom header layout, map the columns in the integration settings.
***
## Columns written
| Column | Description |
| - | - |
| `timestamp` | When the lead was qualified |
| `first_name` | From the contact record |
| `last_name` | From the contact record |
| `phone` | E.164 phone number |
| `email` | If captured during the call |
| `qualification_score` | 0-100 |
| `summary` | AI-generated summary of the call |
| `recording_url` | Link to the audio recording |
| `transcript_url` | Link to the full transcript |
| `campaign` | Campaign name (if applicable) |
***
## Use cases
* **Shared dashboards.** Drop the sheet in a Google Drive folder your team already uses.
* **Hand-off to a different tool.** Google Sheets as a middle layer feeds Looker Studio, a custom Apps Script, or another integration that doesn't have a RevDesk connector yet.
* **Manual review queue.** Sales managers triage the latest qualified leads from the top of the sheet.
***
## Requirements
* Google account with access to the destination spreadsheet
* OAuth authorization (Sheets scope)
* No additional RevDesk cost
***
## Troubleshooting
Confirm the integration is still connected (auth tokens can expire). Check that the AI qualified the call. Only qualified leads write rows.
Open the integration settings and re-map columns. If you've renamed the header row in Google Sheets, the mapping needs to be updated to match.
The Google account you connected needs edit access to the sheet. Re-share the sheet with that account or reconnect with an account that has access.
***
Set up the integration now
# Zapier
Source: https://docs.revdesk.com/integrations/automation/zapier
Connect RevDesk to 8,000+ apps through Zapier. Automate workflows without writing code.
Connect RevDesk to over 8,000 apps through Zapier. Create automated workflows that trigger when calls happen, appointments get booked, or leads are qualified.
***
## What it does
* **8,000+ app connections** - Connect to virtually any tool
* **No-code automation** - Build workflows without coding
* **Trigger on events** - Start workflows from RevDesk events
* **Send leads to RevDesk** - Post a Zap payload to a canonical lead source
***
## Available triggers
Use these RevDesk events to start Zaps:
| Trigger | When it fires |
| - | - |
| New Call | When a call is received |
| Call Completed | When a call ends |
| Appointment Booked | When an appointment is created |
| Appointment Cancelled | When booking is cancelled |
| Appointment Rescheduled | When booking time changes |
| Lead Qualified | When AI qualifies a lead |
| Meeting Ended | When a scheduled meeting completes |
***
## Popular Zaps
### CRM sync
**RevDesk → Google Sheets**
Log every call to a spreadsheet for analysis
### Lead routing
**RevDesk → Email**
Send qualified leads to your sales team instantly
### Follow-up sequences
**RevDesk → Mailchimp**
Add new contacts to email sequences
### Team notifications
**RevDesk → Microsoft Teams**
Post to Teams when appointments are booked
### Log to a spreadsheet
**RevDesk → Google Sheets**
Append a row for every call or booking
***
## Setup
Go to [zapier.com](https://zapier.com) and click "Create Zap"
Search for "RevDesk" in the trigger apps
Choose which RevDesk event starts your workflow
Sign in to RevDesk when prompted
Choose what happens when the trigger fires
Test and activate your automation
## Send a lead into RevDesk
Create a Zapier source in **Directory → Lead sources**, connect it to the tasks that should run, and
copy its generic webhook URL and secret. Use **Webhooks by Zapier → POST** as the Zap action, send the
lead's phone/name/email fields, and authenticate with the source secret. Configure dot-path mapping
on the source when the Zap payload nests those fields. A single source can open work in multiple
tasks; the task, not the source, owns team assignment.
The legacy workflow list, enrollment, and workflow-contact API actions have been removed. For
server-side work creation use `/v1/tasks`; for lead-triggered work use the source webhook. The
deprecated `/v1/jobs` path remains available for existing integrations.
***
## Data available in Zapier
Each trigger includes relevant data:
**Call events include:**
* Caller name, phone, email
* Call duration and timestamp
* Call outcome and disposition
* Transcript and recording links
* Custom fields you've configured
**Appointment events include:**
* Customer details
* Appointment date and time
* Service type
* Location or video link
* Notes from booking
***
## Example workflows
### Qualified lead → Close deal
1. Trigger: Lead Qualified in RevDesk
2. Action: Create deal in Pipedrive
3. Action: Send email to sales rep
4. Action: Add to follow-up sequence
### Appointment booked → Full automation
1. Trigger: Appointment Booked
2. Action: Add to Google Calendar (backup)
3. Action: Send confirmation email
4. Action: Create Trello card for prep
5. Action: Notify team in Slack
***
## Requirements
* Zapier account (Free tier works, paid for multi-step)
* RevDesk account
* No additional cost from RevDesk
***
## Troubleshooting
Check that your Zap is turned on and the trigger event matches your expectations.
Make sure all required fields are available. Test with a real event, not sample data.
Reauthorize the RevDesk connection in Zapier's Connected Accounts.
***
Start building automations now
# Zoho Campaigns
Source: https://docs.revdesk.com/integrations/automation/zoho-campaigns
Auto-configure Zoho Campaigns webhooks so email engagement triggers AI follow-up calls in real time.
Use Zoho Campaigns to send the email, and RevDesk to make the follow-up call. The integration auto-wires the webhooks so engagement events (opens, clicks, replies) trigger AI calls within seconds.
***
## What it does
* **Auto-create webhooks.** RevDesk provisions the Zoho Campaigns webhooks for you. No manual cURL or webhook UI work.
* **Trigger AI calls on engagement.** When a subscriber clicks a link or replies, RevDesk calls them while interest is highest.
* **Track end-to-end.** Campaign sends, opens, clicks, AI calls, and outcomes show up together in the RevDesk inbox so you can see what actually converted.
***
## Setup
Go to **Settings → Integrations → Zoho Campaigns** and click **Connect**. Sign in to Zoho and grant the
campaign webhook scopes.
Select the Zoho campaign(s) you want RevDesk to listen to. Each campaign gets its own webhook so
engagement events are scoped per campaign.
In RevDesk, create an automation: **Trigger → Zoho Campaigns engagement** → choose the event (clicked,
replied) → call the contact.
***
## Events you can listen for
| Zoho event | Typical use |
| - | - |
| Email opened | Light touchpoint. Sometimes too noisy to call on. |
| Link clicked | High-intent signal. Queue the configured next step. |
| Replied | High-priority lead. Route to a rep or transfer live. |
| Unsubscribed | Recorded in RevDesk as an email opt-out. |
***
## Use cases
* **Email × voice campaigns.** Call consented contacts who click.
* **Re-engagement.** Send a "we miss you" email to cold leads. Anyone who opens or clicks gets a follow-up call.
* **Suppression list.** Unsubscribes are recorded in RevDesk.
***
## Requirements
* Zoho Campaigns account with API access enabled
* OAuth authorization with campaign webhook scopes
* No additional RevDesk cost
***
## Troubleshooting
Open the campaign in Zoho and confirm it's actually sending. Webhooks only fire when subscribers engage
with sent emails. Test with a small send to yourself.
Each Zoho Campaigns campaign has its own webhook. Check that the RevDesk lead source mapping filters the
correct Zoho campaign ID, not just "any Zoho event", and that only the intended tasks are connected to
that source.
Zoho OAuth tokens expire if unused. Reconnect from the integration settings, or use the integration
regularly so the refresh token stays warm.
# Apple Calendar
Source: https://docs.revdesk.com/integrations/calendar/apple-calendar
Bookings land on your Apple Calendar over iCloud, with two-way sync across every device on the account.
The agent books on your Apple Calendar over iCloud. Confirmed appointments appear on every device signed into the Apple ID within seconds, including iPhone, iPad, Mac, and Apple Watch. Two-way sync keeps the rep, the agent, and the recipient on the same schedule.
## What changes
* **Real-time availability.** The agent reads free or busy on iCloud Calendar before it offers a time.
* **Bookings written directly.** Events land with caller name, conversation summary, business address or "Phone call," and a link to the transcript.
* **Cross-device sync.** Bookings appear on iPhone, iPad, and Mac the moment the call ends.
* **Multiple calendars.** Different appointment types can land on different iCloud calendars.
## Setup
Apple requires an app-specific password to connect iCloud Calendar to RevDesk. The full flow takes about three minutes.
Inside the app: **Settings → Integrations → Calendar**.
Click "Connect Apple Calendar."
Visit [appleid.apple.com](https://appleid.apple.com), sign in, go to Sign-In and Security, click App-Specific Passwords, generate one named "RevDesk," and copy it.
Paste the app-specific password and your iCloud email into the RevDesk connection prompt.
Pick the iCloud calendar bookings should land on.
Two-factor authentication on your Apple ID is required to generate app-specific passwords. Almost every account already has it; if not, enable it first.
## What gets created on a booking
* **Title.** "Appointment with \[Customer Name]" or your configured template.
* **Description.** Service requested, AI-written conversation summary, link to the transcript.
* **Location.** Business address or "Phone call" depending on appointment type.
* **Link.** The full call recording and transcript in RevDesk.
## Requirements
* iCloud account with Calendar enabled.
* Two-factor authentication enabled on the Apple ID.
* App-specific password (generated during setup).
* No additional cost.
## Troubleshooting
Confirm you're using an app-specific password, not your regular Apple ID password. The two are different.
Confirm iCloud Calendar is enabled in your Apple ID settings.
Confirm the calendar is iCloud-backed, not stored locally on a single device. Local calendars don't sync.
Three-minute setup. The next booked call lands on iCloud Calendar.
# Google Calendar
Source: https://docs.revdesk.com/integrations/calendar/google-calendar
Bookings land on the right Google Calendar before the prospect ends the call. Two-way sync across personal Gmail and Workspace accounts, no double-bookings.
Bookings land on the right Google Calendar before the prospect ends the call. No double-bookings, no copy-pasted times, no email back-and-forth between the agent and the rep. The integration sees real availability, books into the right calendar (yours or a teammate's), and refreshes when the prospect reschedules. Works with personal Gmail and Workspace accounts.
## What changes
* **The agent sees free or busy in real time.** It books into windows that are actually open, including all-day blocks and unconfirmed holds.
* **Events appear instantly.** Confirmed bookings show up on the calendar with caller name, conversation summary, dial-in details, and a link to the transcript.
* **Two-way sync.** Reschedules and cancellations on Google update the RevDesk record, so the rep and the agent share the same view.
* **Multiple calendars supported.** Personal plus work, team calendars for round-robin, service-specific calendars for different appointment types.
## Setup
Inside the app: **Settings → Integrations → Calendar**.
Click "Connect Google Calendar" and sign in to your Google account.
Approve the requested scopes. RevDesk reads free/busy, manages the events it books, and can look up a
customer's existing appointment.
Pick the calendar bookings should land on. You can change this later, or run multiple calendars for
different appointment types.
Configure booking hours, buffer time, and any blocked windows. The agent will respect them on every
booking.
## Permissions
| Permission | What we use it for |
| - | - |
| View calendars and events | Check free or busy windows, list your calendars, find an existing booking. |
| View and edit events | Book, reschedule, or cancel the meeting. |
To offer times, RevDesk reads only whether a window is busy. To find a caller's existing appointment, it
searches your calendar for their email or phone number and reads the matching event's title, time, and
attendees. If calendar sync is on for your workspace, RevDesk also stores upcoming events' title, time,
description, and location to keep availability current.
## What gets created on a booking
* **Title.** "Appointment with \[Customer Name]" or your configured template.
* **Description.** Service requested, AI-written conversation summary, link to the transcript in RevDesk.
* **Location.** Your business address or "Phone call" depending on appointment type.
* **Attendees.** Customer's email when provided, plus the assigned rep.
* **Conferencing.** Google Meet link added automatically when configured.
## Multiple calendars
You can connect more than one calendar for different tasks.
* **Personal plus work.** The agent checks both for conflicts before offering a time.
* **Team calendars.** Round-robin booking distributes meetings across the team.
* **Service-specific.** Different appointment types land on different calendars (e.g., onboarding vs. support).
## Troubleshooting
Disconnect and reconnect from **Settings → Integrations → Google Calendar**. The OAuth token sometimes
expires after a Google security change.
Check that your Google Calendar timezone matches the RevDesk profile timezone. Google Calendar → Settings
→ Time zone.
Only calendars you own or have write access to appear. Check Google Calendar sharing settings if a shared
calendar is missing.
Two-minute setup. The next booked call lands on your calendar.
# ICS Feed
Source: https://docs.revdesk.com/integrations/calendar/ics-feed
Read-only availability from any calendar that exports an ICS feed. Use it for secondary calendars and conflict-checking alongside a direct integration.
Some calendars don't have a direct integration. Most can still publish an ICS feed, which RevDesk reads to block out busy times. It's read-only, so it won't create events. The right way to use it: pair an ICS feed for a secondary calendar (a personal calendar, a shared team calendar from a different provider) with a direct integration for the calendar where bookings should actually land.
ICS is read-only. The agent checks availability and avoids conflicts, but it can't write events. For bookings, connect Google Calendar, Outlook, or Apple Calendar directly.
## What changes
* **Conflict checking.** The agent reads busy windows from any ICS source.
* **Universal compatibility.** Works with anything that exports an ICS URL.
* **Secondary calendar coverage.** Pair with a primary direct integration to honor both your work and personal schedules.
## Setup
In the source calendar's sharing or export settings.
Inside the app: **Settings → Integrations → Calendar**.
Click "Connect ICS Feed."
Enter the ICS feed URL.
RevDesk verifies it can read the feed and confirms how many events were found.
## Finding the ICS URL
| Calendar | Where to find it |
| - | - |
| Google Calendar | Settings → Settings for my calendars → Integrate calendar → Secret address in iCal format. |
| Outlook.com | Settings → Calendar → Shared calendars → Publish a calendar. |
| Apple Calendar | Right-click calendar → Share calendar → Public calendar URL. |
| Yahoo Calendar | Calendar settings → Share → Get sharable link. |
## How it works
The feed fetches periodically (every few minutes). Busy times block those windows in availability, so the agent won't offer a slot that overlaps. Bookings still write to the primary calendar you have connected directly.
## What ICS doesn't do
| Capability | ICS feed | Direct integration |
| - | - | - |
| Check availability | Yes | Yes |
| Create appointments | No | Yes |
| Two-way sync | No | Yes |
| Real-time updates | Minutes delay | Instant |
## When to use ICS
* **Secondary calendars** for conflict checking alongside a primary direct integration.
* **Team availability aggregation** when team members use mixed calendar providers.
* **External systems** that only expose an ICS URL.
## Troubleshooting
ICS syncs every few minutes by design. For instant updates, use a direct integration.
Confirm you're using the ICS/iCal URL, not the HTML calendar link. It should end in `.ics` or contain `/ical/`.
Some ICS exports only include a limited date range. Check the source calendar's export settings.
Pair it with a direct calendar integration for full coverage.
# Outlook and Office 365
Source: https://docs.revdesk.com/integrations/calendar/outlook
Bookings land on the right Outlook calendar before the prospect ends the call. Two-way sync across Outlook.com and Microsoft 365, Teams links included.
The agent books on your Outlook calendar in real time, including shared and room calendars. Two-way sync keeps the rep, the agent, and the recipient on the same schedule. Microsoft Teams meeting links generate automatically when configured. Works with Outlook.com and Microsoft 365 accounts (personal, business, and enterprise tenants).
## What changes
* **Live availability.** The agent reads free or busy on your Outlook calendar before it offers a time.
* **Bookings written directly.** Events land with caller name, conversation summary, location or Teams link, and a link to the transcript.
* **Two-way sync.** Reschedules in Outlook flow back into RevDesk. No drift between systems.
* **Room and shared calendars.** Reserve meeting rooms or book into shared team calendars where the right rep can pick up.
## Setup
Inside the app: **Settings → Integrations → Calendar**.
Click "Connect Outlook / Office 365" and sign in to your Microsoft account.
Approve the requested scopes. RevDesk reads free or busy, manages the events it books, and can look up a
customer's existing appointment.
Pick the calendar bookings should land on. Multiple calendars supported.
Configure booking hours and buffer time. The agent respects them on every booking.
## Microsoft 365 admin approval
Some Microsoft 365 tenants require admin approval for third-party apps before users can grant access. If you see an "admin approval required" prompt, send your IT admin the app details from the integration panel. Once approved, every user in the tenant can connect without further admin involvement.
## Permissions
| Permission | What we use it for |
| - | - |
| `Calendars.Read` | Read free or busy windows, list your calendars, and find an existing booking. |
| `Calendars.ReadWrite` | Create, update, and cancel the events RevDesk books. |
| `User.Read` | Map calendar access to the connecting user. |
| `offline_access` | Keep the connection working without signing in again. |
To offer times, RevDesk reads only whether a window is busy. To find a caller's existing appointment, it
searches your calendar for their email or phone number and reads the matching event's subject, time, and
attendees. If calendar sync is on for your workspace, RevDesk also stores upcoming events' subject, time,
body preview, and location to keep availability current.
## What gets created on a booking
* **Title.** "Appointment with \[Customer Name]" or your configured template.
* **Description.** Service requested, AI-written conversation summary, link to the transcript.
* **Location.** Business address, "Phone call," or a Teams meeting link if conferencing is configured.
* **Attendees.** Customer email when provided, plus the assigned rep.
## Multiple calendars
* **Personal plus work.** Check both for conflicts before offering a time.
* **Shared calendars.** Book into the team's calendar so any rep can pick up.
* **Room calendars (Microsoft 365).** Reserve a meeting room as part of the booking.
## Troubleshooting
Your Microsoft 365 tenant restricts third-party apps. Send your IT admin the app details from the integration panel.
Disconnect and reconnect. The OAuth token sometimes expires after a Microsoft security change.
Verify the Outlook timezone matches the RevDesk profile timezone. Outlook → Settings → Calendar → Time zones.
Two-minute setup. The next booked call lands on your calendar.
# Zoho Calendar
Source: https://docs.revdesk.com/integrations/calendar/zoho-calendar
Bookings land on your Zoho Calendar and link to your Zoho CRM contacts automatically. Best for teams already running on the Zoho suite.
If your team already lives in Zoho, the calendar integration keeps everything where it is. Bookings land on Zoho Calendar, events link to the matching Zoho CRM contact, and Zoho Meeting conference links generate automatically when configured. The agent reads real availability before offering a time, so it never books over an existing event.
## What changes
* **Live availability.** The agent reads free or busy on Zoho Calendar before offering a slot.
* **Bookings written directly.** Events appear with caller name, conversation summary, and a link to the transcript.
* **CRM linkage.** Events tie to the matching Zoho CRM contact so the rep sees the booking and the history side by side.
* **Zoho Meeting links.** Optional video links generate alongside the booking when conferencing is configured.
## Setup
Inside the app: **Settings → Integrations → Calendar**.
Click "Connect Zoho Calendar."
Sign in to your Zoho account and approve the requested scopes.
Pick the Zoho Calendar bookings should land on.
Configure booking hours and buffer time. The agent will respect them.
## What gets created on a booking
* **Title.** "Appointment with \[Customer Name]" or your configured template.
* **Description.** Service requested, AI-written summary, link to the transcript.
* **Location.** Business address, "Phone call," or a Zoho Meeting link when configured.
* **Attendees.** Customer email when provided, plus the assigned rep.
## Zoho suite tie-ins
* **Zoho CRM.** Events link to matching contacts so the booking shows up in the contact timeline.
* **Zoho Bookings.** Coexists with any booking pages you already run, no duplicate slots.
* **Zoho Meeting.** Video links generate alongside the booking when conferencing is enabled.
## Troubleshooting
Disconnect and reconnect from **Settings → Integrations → Zoho Calendar**.
Confirm the calendar is active and not archived in Zoho.
# HubSpot
Source: https://docs.revdesk.com/integrations/crm/hubspot
Sync RevDesk with HubSpot CRM, receive leads from HubSpot workflows, log call results, and create deals.
Connect RevDesk with HubSpot for a bidirectional integration. A HubSpot workflow can deliver a lead to a RevDesk lead source, which opens the connected tasks. RevDesk logs call results back to HubSpot and can create deals for qualified leads.
***
## What it does
* **Receive HubSpot workflow leads** - A HubSpot workflow posts to a RevDesk lead source, and connected tasks begin within seconds
* **Log all calls** - Every AI call is logged to HubSpot with its summary, recording URL, duration, and outcome
* **Auto-create contacts** - New callers are added to HubSpot automatically, existing contacts get updated
* **Sync qualification data** - AI qualification scores, call summaries, and custom properties sync to HubSpot after each call
* **Create deals** - When AI qualifies a lead above your threshold, a deal is created in your pipeline automatically
* **Import and mirror records** - Import HubSpot contacts, companies, and deals; companies and deals you create or edit in RevDesk are written back
***
## Setup
Go to **Settings → Integrations → HubSpot** and click "Connect HubSpot". Sign in and grant permissions for
contacts, deals, and custom properties.
Choose what to sync: call activity logging (default on), custom contact properties (default on), and deal
creation (default off).
Create a HubSpot lead source in **Directory → Lead sources**, map HubSpot contact fields, connect it to a
Speed to Lead task, and copy its webhook URL and secret into the HubSpot workflow.
***
## Trigger calls from HubSpot
Set up a HubSpot workflow to trigger outbound AI calls instantly:
Go to **Directory → Lead sources**, create a HubSpot source, and connect it to a Speed to Lead task.
Copy the webhook URL and secret from the source card. Credentials are visible to workspace admins only.
In HubSpot, create a workflow triggered by your desired event (form fill, deal stage change, etc.).
Add a "Send webhook" action to the workflow. Set the method to POST and paste the RevDesk webhook URL.
Include the contact's phone, email, first name, and last name in the webhook body.
***
## What gets synced back
After every AI call, RevDesk syncs these to HubSpot:
### Call engagement (timeline activity)
| Field | Description |
| - | - |
| Call title | "AI Call: Contact Name" |
| Duration | Call length in seconds |
| Direction | Inbound or outbound |
| Status | Completed, no answer, busy, failed |
| Call body | AI-generated summary of the conversation |
| Recording URL | Link to the call recording |
| From/To numbers | Caller and recipient phone numbers |
### Custom contact properties
| Property | HubSpot Name | Description |
| - | - | - |
| Last AI Call Date | `revdesk_last_ai_call_date` | When the last AI call occurred |
| Qualification Score | `revdesk_qualification_score` | AI qualification score (0-100) |
| Call Summary | `revdesk_call_summary` | AI summary of the last call |
| Total AI Calls | `revdesk_total_ai_calls` | Running count of AI calls |
These properties are created automatically in a "RevDesk" property group. Use them in HubSpot workflows and reports.
### Deals (optional)
When deal creation is enabled and a lead qualifies above your threshold (default: 60), RevDesk creates a deal associated with the contact.
***
## Data sync
| Data | Direction | Trigger |
| - | - | - |
| Contacts | Both ways | On call or import |
| Companies | Both ways | On import or edit |
| Deals | Both ways | On import, edit, or qualification |
| Call activities | RevDesk → HubSpot | After each call |
| Custom properties | RevDesk → HubSpot | After each call |
| Workflow triggers | HubSpot → RevDesk | On HubSpot workflow event |
***
## Use cases
* **Speed to Lead** - Form fill on your website → HubSpot workflow → RevDesk reaches the lead within seconds
* **After-hours coverage** - Inbound calls handled by AI → logged to HubSpot with full details
* **Task outreach** - Give a task HubSpot contacts → AI calling → results sync back to HubSpot
* **Lead qualification** - AI calls → scores the lead → creates deal if qualified → assigns to sales rep via HubSpot workflow
***
## Requirements
* HubSpot account (Free CRM, Starter, or higher)
* Admin or sales permissions in HubSpot
* OAuth authorization with contact + deal + property scopes
* No additional cost from RevDesk
***
## Troubleshooting
Verify the source webhook URL and secret in the HubSpot workflow action. Check the source card for the
latest attempt and mapping error, confirm a connected task is active, and ensure the payload includes a
phone number.
Check that your HubSpot connection has the required scopes. Go to Settings → Integrations → HubSpot. If
you see "Reconnect needed", click to re-authorize with the new permissions.
Click "Setup Custom Properties" in the HubSpot integration settings. RevDesk creates them automatically in
a "RevDesk" property group.
Check that contact sync is enabled in Settings → Integrations → HubSpot → Sync Settings. Contacts are
synced after each call or when imported from HubSpot.
RevDesk deduplicates by email first, then phone number. If a HubSpot contact already exists with the same
email or phone, it's linked rather than duplicated.
***
Set up the integration now
# LeadConnector
Source: https://docs.revdesk.com/integrations/crm/leadconnector
Sync qualified leads and call results back to LeadConnector (GoHighLevel). Contacts are tagged with AICALL so you can trigger CRM workflows.
Connect RevDesk to your LeadConnector (GoHighLevel) sub-account to push call activity and qualified leads back into your CRM, and trigger downstream workflows automatically.
***
## What it does
* **Sync call activity.** Every AI call writes a note to the matching contact in LeadConnector with the summary, recording URL, and outcome.
* **Tag qualified leads.** Contacts that the AI qualifies are tagged with `AICALL` (and any custom tag you configure). Use the tag as a workflow trigger.
* **Match by phone or email.** RevDesk looks up the contact in LeadConnector, updates them if found, and creates them if not.
***
## Setup
Go to **Settings → Integrations → LeadConnector** and click **Connect**. You'll be redirected to
LeadConnector to authorize.
Select the sub-account (location) you want RevDesk to sync with. You can only sync to one per RevDesk
team.
Default tag for qualified leads is `AICALL`. Edit it on the integration settings page if you want a
different label.
***
## What gets synced
| Data | Direction | Trigger |
| - | - | - |
| Contact creation/update | RevDesk → LeadConnector | After each call |
| Call note with summary + recording | RevDesk → LeadConnector | After each call |
| Tags (`AICALL` + custom) | RevDesk → LeadConnector | When the AI qualifies a lead |
## Receive leads from LeadConnector
Create a LeadConnector source in **Directory → Lead sources**, map the contact fields, and connect it
to one or more tasks. In LeadConnector, add a webhook action to the relevant workflow and paste the
source URL and secret. HubSpot/LeadConnector workflow terminology is unchanged: the workflow runs in
the CRM; RevDesk receives it as a lead source. Send a live test and confirm the source card reports a
successful normalized lead before enabling the workflow.
***
## Use cases
* **Re-engage qualified leads.** AI qualifies a lead → tags `AICALL` → LeadConnector workflow sends a confirmation SMS.
* **Notify the sales rep.** AI books a meeting → contact updated in LeadConnector → workflow alerts the assigned rep.
* **Pipeline hygiene.** Every AI conversation logs as a note, so reps see the full history when they open a contact.
***
## Requirements
* LeadConnector / GoHighLevel sub-account with API access
* OAuth authorization (contacts + tags + notes scopes)
* No additional RevDesk cost
***
## Troubleshooting
Confirm you connected the right sub-account. RevDesk creates contacts with the phone number from the call;
if the number is already in another sub-account, you may have linked the wrong one.
Check the qualification criteria on the connected task. The `AICALL` tag only fires when a lead crosses
the qualification bar. Calls that didn't reach that score won't be tagged.
Sync runs at the end of the call. If a call is still in progress or the LeadConnector connection has
expired, the note will retry on the next sync attempt. Reconnect from the integration settings if you see
persistent failures.
***
Set up the integration now
# Zoho CRM
Source: https://docs.revdesk.com/integrations/crm/zoho-crm
Sync RevDesk with Zoho CRM to manage leads, log calls, and automate your sales process.
Connect RevDesk with Zoho CRM to automatically sync call data, deliver leads from Zoho workflows into tasks, and keep your CRM updated in real-time.
***
## What it does
* **Trigger AI calls from Zoho Flow.** Call leads within seconds of a form fill or workflow event.
* **Log every call to Zoho CRM.** Duration, outcome, transcript, and summary on the contact timeline.
* **Auto-sync contacts.** New callers added to Zoho, existing contacts updated with call outcomes.
* **Create deals for qualified leads.** When AI qualifies a lead above your threshold, a deal is created.
* **Use Zoho contacts in tasks.** Pull contacts directly for outbound AI calling.
* **Custom fields.** RevDesk updates qualification score, call summary, and last call date.
***
## Setup
Navigate to **Settings → Integrations → CRM**
Click "Connect Zoho CRM" and complete the setup wizard
Sign in to your Zoho account and grant permissions. The existing OAuth scopes (`ZohoCRM.modules.ALL`)
cover all AI calling features.
Choose what to sync: call activity logging, custom contact fields, and auto-deal creation
Connect Zoho Flow to trigger outbound AI calls from CRM events
***
## Trigger calls from Zoho Flow
Use Zoho Flow or CRM workflows to trigger instant AI calls when events happen in your CRM.
In RevDesk, go to **Directory → Lead sources**, create a Zoho CRM / Flow source, and connect it to a Speed to Lead task.
Copy the webhook URL and secret from the source card.
In Zoho Flow, create a new flow with your desired trigger (e.g., "New Lead in Zoho CRM").
Add a "Webhooks" action to the flow. Set the method to **POST** and paste the webhook URL.
Set the JSON body with contact data:
```json theme={null}
{
"name": "{{First Name}} {{Last Name}}",
"phone": "{{Phone}}",
"email": "{{Email}}"
}
```
Add the `X-Automation-Signature` header with the HMAC-SHA256 of the request body using the webhook secret.
***
## Use Zoho contacts in tasks
Pull contacts from Zoho CRM into an outbound RevDesk task:
1. Create or open an outbound task
2. In the People step, click **Import from Zoho CRM**
3. Search and select the contacts you want to call
4. Selected contacts are imported to RevDesk and added to the task
Contacts are deduplicated by phone number. Existing contacts are linked to Zoho CRM rather than duplicated.
***
## What gets synced back
After each AI call, RevDesk syncs the following to Zoho CRM:
### Call Activity
Every call creates a Zoho CRM Call record with:
| Field | Description |
| - | - |
| Subject | "AI Call: \{Contact Name}" |
| Call Type | Inbound or Outbound |
| Duration | Call length (hh:mm:ss) |
| Start Time | When the call started |
| Description | AI-generated call summary |
| Who | Linked to the Zoho CRM contact |
### Custom Contact Fields
RevDesk creates and updates these custom fields on each contact:
| Field | Type | Description |
| - | - | - |
| RevDesk\_Last\_AI\_Call\_Date | DateTime | When the last AI call occurred |
| RevDesk\_Qualification\_Score | Integer | Lead qualification score (0-100) |
| RevDesk\_Call\_Summary | Text | Summary of the last AI call |
| RevDesk\_Total\_AI\_Calls | Integer | Running count of AI calls |
### Deal Creation
When enabled and a lead scores above your threshold, RevDesk creates a Zoho CRM Deal with:
* **Deal Name**: "AI Qualified Lead: \{Contact Name}"
* **Stage**: Your configured stage (default: "Qualification")
* **Description**: AI call summary
* **Contact**: Linked to the Zoho CRM contact
***
## Use cases
* **Speed to Lead.** New web form submission triggers an AI call within seconds.
* **After-hours handling.** Missed call triggers an AI callback immediately.
* **Campaign outreach.** Import Zoho contacts into a campaign for bulk AI calling.
* **Lead qualification.** AI qualifies leads and pushes scored results back to Zoho CRM.
* **Re-engagement.** Contact field update in Zoho triggers a follow-up call.
***
## Data sync
| Module | Direction | When |
| - | - | - |
| Contacts | Both ways | After each call / on import |
| Calls | RevDesk → Zoho | After each call |
| Deals | RevDesk → Zoho | When lead is qualified |
| Custom Fields | RevDesk → Zoho | After each call |
***
## Requirements
* Zoho CRM account (any edition)
* API access enabled (included in most editions)
* Admin permissions for initial setup
* No additional cost from RevDesk
***
## Troubleshooting
Make sure you have admin permissions and API access is enabled for your Zoho account. If you're using a
data center outside the US, the integration handles multi-region automatically via `accountServer`.
Check that the integration is still connected in Settings → Integrations. Zoho OAuth tokens expire after
\~60 minutes and are refreshed automatically, but if the refresh token is revoked, you'll need to
reconnect.
Run the field setup again from the integration settings. Custom fields require admin-level API permissions
in Zoho CRM.
Zoho CRM has API rate limits (typically 5000 requests/day for free plans). If you see rate limit errors in
the sync logs, consider upgrading your Zoho plan or reducing sync frequency.
RevDesk deduplicates contacts by phone number. If duplicates appear, check that phone numbers are
formatted consistently (include country code).
# Integrations
Source: https://docs.revdesk.com/integrations/index
Connect supported calendars, CRMs, conferencing providers, messaging channels, and automation tools to RevDesk.
# Connect the systems a workflow needs
Use an integration when another system owns availability, customer records, meetings, messages, or downstream
automation. Connect only the systems required by the workflow and decide which system owns every field that
can be written in both places.
Availability varies by product, workspace, country, provider account, and deployment. **Settings →
Integrations** is the source of truth for what your workspace can connect.
## Calendar
Calendar connections provide availability and create bookings. Review the individual guide for sync direction,
permissions, and provider-specific limitations.
Google personal and Workspace calendars.
Outlook.com, Microsoft 365, and supported Exchange accounts.
Supported iCloud calendar availability and booking behavior.
Read-only availability from a published ICS feed.
## Conferencing
Conferencing integrations can add a meeting link to a supported calendar booking.
Create Meet links through a connected Google calendar.
Create Teams links through a supported Microsoft 365 connection.
## CRM
CRM behavior depends on the connected provider and configured workflow. Before enabling write-back, document
record matching, field ownership, duplicate handling, and recovery.
Connect supported HubSpot contacts, workflows, and activity write-back.
Connect supported LeadConnector locations and record updates.
## Messaging and notifications
Customer messaging requires a supported sender, recipient capability, and applicable consent. Slack is an
internal notification integration, not a customer conversation channel.
Configure supported business iMessage conversations.
Configure supported WhatsApp Business messaging.
Verify a sending domain and configure reusable templates.
Route supported internal notifications to Slack channels.
## Automation and other tools
Connect supported triggers and actions to other services.
Send supported workflow data to a sheet.
Review BAA availability and healthcare configuration.
Configure supported GIF content on customer-facing surfaces.
## Build a custom connection
Use [Zapier](/integrations/automation/zapier) for supported no-code workflows. For a custom service, start with
[Build an integration](/developer-guide/build-an-integration) and the [API Reference](/api-reference).
# Email
Source: https://docs.revdesk.com/integrations/messaging/email
Configure your sending domain, verify DNS, and create reusable templates so your campaigns and follow-ups land in the inbox.
Email isn't a third-party CRM. It's built into RevDesk. This page covers how to set up your sending domain, verify DNS records, and manage templates for campaigns and follow-ups.
***
## What it does
* **Custom sending domain.** Send from `you@yourbusiness.com` instead of a generic RevDesk address. Better deliverability, on-brand.
* **DNS verification.** Guided SPF, DKIM, and DMARC setup with copy-paste records.
* **Template library.** Save and reuse subject lines, body copy, and signature blocks across campaigns.
* **Per-campaign overrides.** Every campaign can customize the sender name, reply-to address, and template.
***
## Setup
Go to **Settings → Integrations → Email**.
Click **Add domain** and enter the domain you want to send from (e.g. `yourbusiness.com`). RevDesk generates the SPF, DKIM, and DMARC records you need.
Log in to your DNS provider (Cloudflare, GoDaddy, Namecheap, etc.) and add the three records exactly as shown. Click **Verify** in RevDesk once they're live. Propagation usually takes 5 to 30 minutes.
Build templates for your most common use cases: campaign sends, missed-call follow-ups, booking confirmations, review requests.
***
## DNS records
You'll add three TXT records to your DNS:
| Record | Purpose |
| - | - |
| **SPF** | Authorizes RevDesk to send mail on behalf of your domain |
| **DKIM** | Cryptographically signs your outbound mail to prove authenticity |
| **DMARC** | Tells receiving servers what to do with mail that fails SPF/DKIM |
All three are required for production sending. RevDesk won't let you send from an unverified domain.
***
## Templates
Templates support variables: `{{firstName}}`, `{{businessName}}`, `{{bookingLink}}`, and any custom field you've defined on a campaign contact.
Common templates worth setting up:
* **Cold outreach.** Opening email in an email × voice campaign.
* **Missed call follow-up.** Sent automatically when an inbound caller hangs up before booking.
* **Booking confirmation.** Sent right after the AI books a meeting.
* **Review request.** Sent N days after a completed service.
***
## Per-campaign overrides
Inside any campaign, the **Sender** section lets you override:
* Sender display name (`Jane @ Acme HVAC`)
* Reply-to email (route replies to a real human)
* Template (or write fresh copy for this campaign)
***
## Troubleshooting
DNS changes can take up to a few hours depending on your provider. Use `dig TXT yourbusiness.com` to confirm the record is live before clicking Verify again.
Most often a DMARC/DKIM misconfiguration. Make sure all three records are verified, then warm the domain by sending small batches for the first week.
Set the **Reply-to** address on the campaign or in your default sender settings. Without it, replies route to a no-reply RevDesk address.
***
Set up your sending domain and templates
# iMessage
Source: https://docs.revdesk.com/integrations/messaging/imessage
Configure supported iMessage conversations, sender identity, replies, consent, and troubleshooting.
iMessage is available as a limited Photon-powered pilot for the RevDesk workspace. It supports manual,
two-way text conversations in the Inbox. Replies attach to the corresponding RevDesk contact and conversation.
## What changes
* **Photon-owned sender.** The pilot uses the iMessage address assigned by Photon; it is separate from
your RevDesk/Telnyx SMS number.
* **Manual text conversations.** Team members can send and receive plain-text iMessages from the Inbox.
* **One shared timeline.** Inbound and outbound iMessages appear on the associated contact.
* **Consent-aware sending.** Opt-outs update the contact's applicable channel permissions and block later
actions that are no longer allowed.
## Setup
Photon setup is managed by a RevDesk platform administrator during the pilot.
The administrator binds one Photon project and one active Photon-owned iMessage sender to the workspace.
Send a manual Inbox message, reply from the recipient, and confirm both messages appear on the same
contact.
## Standard flows
| Scenario | Pilot behavior |
| - | - |
| Manual Inbox reply | Queued and sent through the workspace's Photon sender. |
| Customer text reply | Verified, deduplicated, and written to the same contact conversation. |
| Unknown phone number | Creates an organization-scoped contact for the inbound conversation. |
| Job or automated send | Blocked during the pilot. |
| Delivery failure | Shown as an iMessage failure; never silently retried as SMS. |
## Two-way replies
Customer replies open a conversation with the configured owner when one is assigned. STOP suppresses
phone messaging and iMessage; START records iMessage consent. Attachments, tapbacks, typing indicators,
read receipts, and automated replies are outside the pilot.
## Troubleshooting
This is intentional. RevDesk does not silently change the sender identity, compliance rules, or channel.
Send an SMS separately only after reviewing consent and choosing an SMS sender.
The last iMessage to this contact failed because the number can't receive iMessage (for example, an
Android phone). The channel picker shows a warning, but iMessage stays available, so you can try again or
send a text instead. The warning clears after a message gets through in either direction.
The workspace sends from a shared Photon line, which only reaches numbers registered on the Photon
project. Ask a platform administrator to add the number or assign a dedicated line, then send a new
message.
The workspace does not have exactly one healthy Photon sender. Ask a platform administrator to inspect the
connection and webhook status.
Check the contact's timeline for an opt-out event. A phone STOP suppresses calls, SMS, and iMessage for
that number.
Contact RevDesk to enable the workspace-managed pilot.
# Slack
Source: https://docs.revdesk.com/integrations/messaging/slack
Get real-time RevDesk notifications in Slack channels. Know instantly when calls come in and appointments get booked.
Get instant notifications in Slack when calls come in, appointments are booked, or important events happen. Keep your team in the loop without checking RevDesk constantly.
***
## What it does
* **Real-time notifications** - Know instantly when something happens
* **Channel routing** - Send different notifications to different channels
* **Rich messages** - See call details, transcripts, and action buttons
* **Team awareness** - Everyone sees what's happening
***
## Setup
Navigate to **Settings → Integrations → Messaging**
Click "Connect Slack"
Sign in to your Slack workspace and grant permissions
Select which channels receive notifications
Choose which events trigger notifications
***
## Notification types
Choose which events send Slack notifications:
| Event | Example message |
| - | - |
| New call | "Incoming call from John Smith (555-123-4567)" |
| Appointment booked | "New appointment: AC Repair with John Smith, Tomorrow 2pm" |
| Lead qualified | "Lead qualified: John Smith is interested in HVAC service" |
| Voicemail | "New voicemail from 555-123-4567 (45 seconds)" |
| Missed call | "Missed call from 555-123-4567" |
***
## Channel routing
Send different notifications to different channels:
* **#sales** - New leads and qualified prospects
* **#support** - Customer service calls
* **#appointments** - All booking confirmations
* **#urgent** - Emergency calls only
***
## Rich notifications
Slack messages include:
* **Caller info**: Name, phone, company
* **Call summary**: What was discussed
* **Quick actions**: Call back, view transcript, assign
* **Transcript link**: One-click to full details
***
## Team mentions
Configure @mentions for urgent situations:
* **@channel** for emergency calls
* **@person** when specific team member needed
* **@here** for time-sensitive notifications
***
## Requirements
* Slack workspace
* Permission to add apps
* No additional cost from RevDesk
***
## Troubleshooting
Check that the channel is selected and notifications are enabled for that event type.
Update channel routing in Settings → Integrations → Slack → Channels.
You need permission to add apps. Contact your Slack admin.
***
Set up the integration now
# Telegram
Source: https://docs.revdesk.com/integrations/messaging/telegram
Run two-way customer conversations on Telegram with the same agent, memory, and compliance rules as every other channel.
For customers who live on Telegram, the agent meets them there. Conversations run two-way with media and one-tap buttons, and everything writes to the same RevDesk contact record as your voice, iMessage, and SMS threads.
## What you can do
* **Two-way conversations.** Questions answered, appointments booked, reschedules handled in-thread.
* **Rich replies.** Media, links, and button prompts.
* **One thread.** Telegram messages land on the same contact timeline as every other channel.
## Setup
Inside the app: **Settings → Integrations → Messaging**.
Link your business's Telegram presence and confirm ownership.
Choose which agent handles Telegram threads and when to escalate to your team.
See the full channel picture in [Messaging channels](/getting-started/features/messaging).
# WhatsApp
Source: https://docs.revdesk.com/integrations/messaging/whatsapp
Send confirmations, reminders, and follow-ups over WhatsApp. Two-way replies route through the same RevDesk contact record.
For many customers, especially outside the US, WhatsApp is the app they read first. The integration sends booking confirmations, reminders, and review requests over WhatsApp Business. Replies route into the same RevDesk contact record so the agent and the rep see the full thread. In the US, [iMessage](/integrations/messaging/imessage) is another option — the agent picks the best available channel per contact. See [Messaging channels](/getting-started/features/messaging) for the full picture.
## What changes
* **Confirmations land where the customer reads.** Right after a booked call, the WhatsApp confirmation reaches them with the appointment details and address.
* **Reminder cadence.** 24-hour and one-hour reminders fire automatically.
* **Two-way replies.** Customer responses come back into RevDesk. The agent can answer, or escalate to your team if intent spikes.
* **Consent-aware sending.** Opt-outs update the contact's applicable channel permissions and block later
actions that are no longer allowed.
## Setup
Inside the app: **Settings → Integrations → Messaging**.
Sign in with the WhatsApp Business account that owns your phone number.
WhatsApp confirms the business phone number through their verification flow.
Build templates for each scenario, such as confirmation, reminder, or follow-up. Submit them for provider
review and do not launch until the required template is approved.
## Standard templates
| Scenario | Template behavior |
| - | - |
| Confirmation | Sent right after a booked call. Includes appointment time, address, and a reschedule link. |
| 24-hour reminder | "Your appointment is tomorrow at 2pm." |
| 1-hour reminder | "Your appointment starts in 1 hour." |
| Follow-up | "Thanks for stopping by. Would you leave a quick review?" |
| Reschedule | One-click reply that routes back to a live agent for a new time. |
## WhatsApp Business requirements
* **Business account.** Personal WhatsApp accounts can't be used.
* **Verified phone number.** Through WhatsApp's verification flow.
* **Approved templates.** Business-initiated messages require pre-approved templates.
* **Compliance.** Messages must follow WhatsApp's commerce and messaging policies.
## Two-way replies
When customers reply to a WhatsApp message, the response opens a 24-hour free-form messaging window with that contact. During the window, RevDesk can:
* Answer common questions automatically.
* Reschedule the appointment.
* Route the conversation to a human if intent spikes.
* Stay in thread without sending another approved template.
Outside the 24-hour window, business-initiated messages require an approved template.
## Troubleshooting
Confirm the template is approved and the customer's number is valid in WhatsApp's directory.
Strip promotional language from transactional templates. WhatsApp rejects templates that read as marketing
pushes.
The customer needs WhatsApp installed and the number on the message must match a registered account.
Five-minute setup. Templates typically approve in a day.
# Giphy
Source: https://docs.revdesk.com/integrations/other/giphy
Add GIFs to your RevDesk booking pages and confirmation messages for a more engaging experience.
Add animated GIFs to your booking confirmation pages and messages. Make your customer experience more memorable and engaging.
***
## What it does
* **Booking confirmations** - Add celebratory GIFs when appointments are booked
* **Reminder messages** - Include fun GIFs in reminder notifications
* **Custom pages** - Use GIFs on your branded booking pages
* **Search library** - Access millions of GIFs from Giphy
***
## Use cases
### Appointment confirmed
Show a celebration GIF when booking is complete:
* "You're all set!" with confetti animation
* Service-specific GIFs (thumbs up, checkmark, etc.)
* Brand-aligned animations
### Reminder messages
Make reminders more engaging:
* Friendly wave GIF in reminder emails
* Calendar animation in SMS reminders
* "See you soon!" animated stickers
***
## Setup
Navigate to **Settings → Integrations → Other**
Click "Enable Giphy"
Choose appropriate content level (G, PG, PG-13)
Select where GIFs should appear
***
## Content ratings
Control what GIFs are available:
| Rating | Content type |
| - | - |
| G | Family-friendly, safe for all |
| PG | Mild content, generally safe |
| PG-13 | Some mature themes |
Recommended: **G** for business use.
***
## Where GIFs appear
Enable GIFs on:
* **Booking confirmation page** - After appointment is scheduled
* **Confirmation emails** - In the appointment confirmation
* **Reminder messages** - Optional fun addition
* **Custom booking pages** - Your branded landing pages
***
## Customization
Choose GIF behavior:
* **Random from category** - Different GIF each time from a theme
* **Specific GIF** - Same GIF always (by Giphy ID)
* **Search term** - Random from search results (e.g., "celebration")
***
## Requirements
* RevDesk account
* No Giphy account required (uses Giphy API)
* No additional cost
***
## Troubleshooting
Check that Giphy is enabled and content rating allows the selected GIF.
Lower your content rating to G for strictest filtering.
Find the GIF on giphy.com, copy its ID, and paste in RevDesk settings.
***
Set up the integration now
# HIPAA Business Associate Agreement
Source: https://docs.revdesk.com/integrations/other/hipaa
Business Associate Agreement for healthcare organizations. Handle protected health information compliantly.
For healthcare organizations handling Protected Health Information (PHI), RevDesk offers a Business Associate Agreement for HIPAA workloads.
***
## What it does
* **Legal coverage** - BAA establishes compliant data handling
* **PHI protection** - Appropriate safeguards for health information
* **Audit support** - Documentation for compliance audits
* **Peace of mind** - Clear responsibilities and protections
***
## Who needs this
A BAA is required if you:
* **Are a healthcare provider** - Doctors, dentists, therapists, clinics
* **Handle PHI** - Patient names, conditions, appointments
* **Transmit health info** - Via calls, texts, or emails
* **Are a business associate** - Handle PHI for covered entities
***
## What's covered
The BAA covers RevDesk's handling of:
| Data type | Examples |
| - | - |
| Patient identifiers | Names, phone numbers, addresses |
| Health information | Conditions mentioned in calls |
| Appointment data | Medical appointments, providers |
| Communication records | Call transcripts, voicemails |
***
## How to get a BAA
Email [support@revdesk.com](mailto:support@revdesk.com) with subject "HIPAA BAA Request". Include your legal entity name, the workspace
that will handle PHI, and a primary compliance contact.
Confirm you're a covered entity or business associate
We send our standard BAA within 2 business days. Customers who can sign as-is get same-day activation;
legal markup typically closes in 5–10 business days.
Sign electronically and receive countersigned copy
We record `baaSignedAt` on your workspace and flip `hipaa_enabled = true`. Runtime controls activate
immediately.
***
## HIPAA mode features
When BAA is active, RevDesk enables:
* **BAA-eligible voice routing** - AI voice runs only on BAA-eligible providers
* **Webhook redaction** - Recordings and transcripts are stripped from outbound webhooks
* **API redaction** - Recordings and transcripts are stripped from the public API
***
## Compliance responsibilities
### RevDesk responsibilities
* Secure data storage and transmission
* Access controls and authentication
* Breach notification
* Subcontractor agreements
### Your responsibilities
* Authorized use of RevDesk
* User access management
* Minimum necessary data sharing
* Patient authorization when required
***
## Pricing
BAA terms, including any cost, are confirmed when you request the agreement.
### Platform-wide HIPAA for channel partners
If you're a partner placing healthcare customers on RevDesk at consistent volume, we offer a **platform-wide HIPAA posture**. [Contact sales](https://cal.com/revdesk/30min) for pricing. Your accounts become HIPAA-compatible by default, and we sign BAAs with your downstream customers individually as they need them.
***
## Requirements
* An active RevDesk plan
* Signed BAA before handling PHI
* HIPAA training for your staff
* Compliance policies in place
***
## FAQ
No. BAAs are only required when handling PHI. General business use doesn't require one.
Yes, with an active BAA. RevDesk can send appointment reminders that include patient information.
RevDesk will notify you within 24 hours of discovering any potential breach, as required by the BAA.
The BAA covers call handling and scheduling. For video visits, ensure your telehealth platform is also
compliant.
***
Email [support@revdesk.com](mailto:support@revdesk.com) to get started
# AI Forms
Source: https://docs.revdesk.com/integrations/other/routing-forms
Intelligent call routing based on form responses. Direct callers to the right team or service automatically.
Create smart routing forms that direct callers to the right team, calendar, or service based on their responses. Qualify and route before the call even starts.
***
## What it does
* **Pre-call qualification** - Ask questions before routing
* **Smart routing** - Direct to right team/calendar based on answers
* **Service matching** - Route to specialists by service type
* **Capacity management** - Balance load across team members
***
## Use cases
### Service-based routing
Route callers by service needed:
* "AC Repair" → HVAC technician calendar
* "Plumbing" → Plumber calendar
* "Electrical" → Electrician calendar
### Location-based routing
Route by service area:
* Within 20 miles → Local team
* 20-50 miles → Regional team
* 50+ miles → "Not in service area" message
### Urgency routing
Route by urgency level:
* Emergency → On-call technician
* Same day → Available technician
* Scheduled → Any available slot
### Lead qualification routing
Route by business fit:
* Enterprise → Sales director
* SMB → Account executive
* Consumer → Self-service booking
***
## Setup
Navigate to **Settings → Routing Forms**
Click "Create Routing Form"
Build your qualification questions
Set routing rules based on answers
Link routes to calendars or teams
***
## Question types
Build forms with various question types:
| Type | Example |
| - | - |
| Multiple choice | "What service do you need?" |
| Location | "What's your zip code?" |
| Yes/No | "Is this an emergency?" |
| Text | "Briefly describe the issue" |
| Number | "How many units need service?" |
***
## Routing logic
Create routing rules:
```
IF service = "Emergency" AND location in service_area
THEN route to on_call_calendar
IF service = "AC Repair"
THEN route to hvac_team
IF budget > $5000
THEN route to senior_sales
DEFAULT
THEN route to general_calendar
```
***
## Destinations
Route to various destinations:
* **Specific calendar** - Individual team member
* **Round-robin** - Distribute across team
* **Weighted routing** - More to senior members
* **Time-based** - Different routes by time of day
* **Capacity-based** - Route to available capacity
***
## Form embedding
Embed routing forms:
* **Website** - Embed on your contact page
* **Booking pages** - Add as first step
* **Phone IVR** - AI asks questions from form
* **SMS** - Send form link via text
***
## Requirements
* RevDesk account
* Multiple calendars or team members (for routing value)
* No additional cost
***
## Troubleshooting
Check your routing rules. Rules are evaluated in order; first match wins.
Review your question wording. Ambiguous questions lead to wrong routes.
Check that routed calendar has availability configured.
***
Set up intelligent routing now
# Google Meet
Source: https://docs.revdesk.com/integrations/video/google-meet
Booked appointments get a Google Meet link automatically. The customer joins from the calendar invite, no setup on their side.
When the agent books a meeting, a Google Meet link generates and lands in the calendar invite. The customer joins from the invite with one click. The rep doesn't have to create the link. The agent doesn't have to read it out loud. The Google Meet integration runs on top of the Google Calendar connection, so Google Calendar must be connected first.
## What changes
* **Meet link on every booking.** Generated automatically inside the calendar event.
* **One-click join.** From the invite, the device, or Google Calendar directly.
* **Workspace features pass through.** Recording, breakout rooms, and live captions follow your Workspace tier.
* **Per-type control.** Apply Meet links to specific appointment types (e.g., consultations) and leave others as phone-only.
## Setup
See [Google Calendar](/integrations/calendar/google-calendar). Meet runs on top of the calendar connection.
Inside the app: **Settings → Integrations → Video**.
Click "Enable Google Meet."
Choose which booking types should include a Meet link.
## What the customer sees
The calendar invite includes the Meet link prominently, plus the dial-in fallback. Most customers join from the calendar invite directly. The agent doesn't need to read the URL out loud during the call.
## Google Workspace features
| Feature | Available on |
| - | - |
| Domain-branded Meet URLs | Workspace |
| Recording | Business Standard and higher |
| Breakout rooms | Business Plus and higher |
| Extended meeting times | Paid Workspace tiers |
## Per-type configuration
Not every appointment needs video. Configure where Meet links go:
* **All appointments.** Every booking includes a link.
* **Specific types.** Only consultations, only enterprise demos, only your call.
* **On request.** The agent asks the customer if they prefer video before booking.
## Troubleshooting
Confirm Google Calendar is connected and Meet is enabled for this appointment type.
Google Calendar must be connected first. **Settings → Integrations → Calendar.**
Two-minute setup once Google Calendar is connected.
# Microsoft Teams
Source: https://docs.revdesk.com/integrations/video/microsoft-teams
Booked appointments get a Microsoft Teams meeting link automatically. The customer joins from the Outlook invite, no setup on their side.
When the agent books a meeting, a Teams meeting link generates and lands in the Outlook calendar invite. The customer joins from the invite with one click. The rep doesn't have to create the link or copy it over. The Teams integration runs on top of the Outlook calendar connection, so Outlook must be connected first.
## What changes
* **Teams link on every booking.** Generated automatically inside the Outlook event.
* **One-click join.** From the invite, the Teams app, or Outlook directly.
* **Microsoft 365 features pass through.** Recording, live captions, and breakout rooms follow your Microsoft 365 plan.
* **Per-type control.** Apply Teams links to specific appointment types and leave others as phone-only.
## Setup
See [Outlook and Office 365](/integrations/calendar/outlook). Teams runs on top of the calendar connection.
Inside the app: **Settings → Integrations → Video**.
Click "Enable Microsoft Teams" and approve the additional scope for creating Teams meetings.
Choose which booking types should include a Teams link.
## Admin approval
Some Microsoft 365 tenants require admin approval for third-party apps. If the connect flow shows "admin approval required," send your IT admin the app details from the integration panel. Once approved, every user in the tenant can connect without further admin involvement.
## Microsoft 365 features
| Feature | Available on |
| - | - |
| Recording | Business Basic and higher |
| Live captions | Business Basic and higher |
| Breakout rooms | Business Standard and higher |
| Familiar Teams UI | Anyone with a Teams account |
## Per-type configuration
Not every appointment needs video. Configure where Teams links go:
* **All appointments.** Every booking includes a link.
* **Specific types.** Only consultations, only enterprise demos, only your call.
* **On request.** The agent asks the customer if they prefer video before booking.
## Troubleshooting
Outlook must be connected first. **Settings → Integrations → Calendar.**
Send your IT admin the app details from the integration panel. They approve once for the tenant.
Confirm Teams is enabled for this appointment type in settings.
Two-minute setup once Outlook is connected.
# Code Review Process
Source: https://docs.revdesk.com/policies/code-review-process
How RevDesk (Cell Labs, Inc.) reviews code changes before they reach production. Public reference for the procedure cited in our Secure Development Policy.
This page describes the code review procedure referenced in the Cell Labs, Inc. Secure Development Policy. It applies to every change merged into a production branch of any business-critical RevDesk system.
***
## Scope
The procedure applies to all source code repositories owned by Cell Labs, Inc. that build, deploy, or run any of the following:
* The RevDesk web application and API
* Customer-facing infrastructure (voice, telephony, calendar sync, CRM sync, billing)
* Internal services that handle Confidential or customer data
* Build, deployment, or CI/CD configuration that affects production
Documentation-only changes, marketing site copy, and ephemeral experimental branches are reviewed but may follow a lighter cadence.
***
## Required controls
### 1. Pull requests are required
All changes that target a production branch are made via a pull request on GitHub. Direct pushes to `main` and `prod` are disabled at the repository level. There is no "emergency bypass" path that skips review.
### 2. At least one reviewer other than the author
Every pull request must receive an approving review from at least one engineer who is not the author before it can be merged. Branch protection enforces this; the merge button is disabled until the approval is recorded.
For changes that touch authentication, billing, telephony provisioning, or PHI handling, the reviewer must be a senior engineer or the CTO.
### 3. Continuous integration must pass
The following CI checks must complete successfully on the pull request's head commit before merge is allowed:
* TypeScript type checking (`tsgo --noEmit`)
* ESLint
* Unit and integration tests
* Build of the production bundle
Pre-commit hooks run a subset of these locally to surface failures earlier. Hooks are not skipped (`--no-verify` is prohibited).
### 4. Preview deployment must build
Vercel runs a preview deployment for every pull request. The preview must build successfully and pass automated smoke checks before the PR is eligible for merge. Reviewers also use the preview URL to spot-check user-facing behavior.
### 5. Separation of duties
Development, testing, and deployment of a change are not performed by a single individual without oversight. The author opens the PR; a separate reviewer approves it; production deploys (including database migrations) are visible to the engineering team and tracked in deployment history.
***
## What reviewers check
Reviewers look for:
* **Correctness.** Does the code do what the description says it does? Are edge cases handled?
* **Security.** OWASP Top 10 sensitivity: input validation at system boundaries, no SQL injection, no XSS, no broken access control, no secrets in code or logs.
* **Coding standards.** Adherence to the codebase's style and patterns: type safety (no `as any`), explicit error handling (`ErrorWithCode` in non-tRPC paths), Drizzle at the call site with narrowed column selection (never over-fetch), repository-and-DTO separation, no barrel imports.
* **Tests.** New behavior is covered by tests appropriate to the change. Bug fixes include a regression test where feasible.
* **Comments and naming.** Comments explain *why* when the reason is non-obvious; identifiers are self-describing.
* **Backwards compatibility.** Database schema changes, API contracts, and feature flag transitions are designed to be safe under concurrent deploys.
***
## Hotfixes and emergencies
Hotfixes follow the same review path as any other change: pull request, approval, CI green, preview build, merge. The expedited cases are reviewed quickly, not skipped.
If a production incident genuinely requires bypassing the standard procedure (e.g. reverting a deploy that is actively breaking customers), the bypass requires explicit CTO approval, must be documented in the incident timeline, and is followed by a retrospective pull request that brings the change under the standard review process.
***
## Outsourced and contractor development
Any contractor or outsourced developer working on Cell Labs, Inc. code is bound by the same procedure. They open pull requests against the same repositories and their changes are reviewed by a Cell Labs employee before merge.
***
## Version control
All source code is stored in centralized Git repositories owned by Cell Labs, Inc. Access is granted by role (engineering, contractor, read-only) and revoked promptly on offboarding. Branch protection, required reviews, and required status checks are enforced at the repository level.
***
## Related
* [Engineering Documentation](/policies/engineering-documentation): secure-by-design and privacy-by-design principles applied across the codebase.
* [Privacy Policy](https://revdesk.com/privacy)
* [Terms of Service](https://revdesk.com/terms)
# Engineering Documentation
Source: https://docs.revdesk.com/policies/engineering-documentation
Secure-by-design and privacy-by-design principles applied across RevDesk (Cell Labs, Inc.) engineering, plus environment, vulnerability management, and developer training references.
This page is the public reference for the engineering documentation cited in the Cell Labs, Inc. Secure Development Policy. It summarizes the principles applied across all business-critical RevDesk systems and the controls that surround them.
***
## Secure-by-design principles
These principles guide every design and code review at Cell Labs, Inc.
* **Minimize attack surface.** Every new feature deliberately limits the inputs it accepts, the actions it exposes, and the data it stores. Unused endpoints, scopes, and permissions are removed.
* **Establish secure defaults.** New features ship with the most restrictive viable configuration. Customers opt in to looser settings, not out of safer ones.
* **Least privilege.** Application code, infrastructure roles, third-party integrations, and human operators receive the minimum permissions needed for the task. Credentials are scoped per role and rotated.
* **Defense in depth.** No single control is relied upon. Authentication, authorization, input validation, output encoding, network segmentation, and audit logging are layered.
* **Fail securely.** Errors do not leak data or grant access. A failed authorization check denies the request; a failed dependency does not bypass safeguards.
* **Don't trust services.** External services, including paid integrations, are treated as untrusted inputs. Responses are validated, retries are bounded, and timeouts are enforced.
* **Separation of duties.** Authors do not approve their own changes; admins cannot unilaterally exfiltrate data without producing an audit trail.
* **Avoid security by obscurity.** Controls do not depend on the secrecy of design, only on the secrecy of keys.
* **Keep security simple.** Simpler controls are easier to verify. Complex schemes are reviewed especially carefully.
* **Fix security issues correctly.** Security fixes address the root cause, include a regression test where feasible, and are reviewed with the same rigor as any production change.
***
## Privacy-by-design principles
These principles, derived from Ann Cavoukian's framework, apply to every system that processes customer or end-caller data.
* **Proactive, not reactive; preventative, not remedial.** Privacy risks are addressed in design, not patched after incidents.
* **Privacy as the default setting.** Data collection, retention, and sharing default to the minimum required to deliver the feature. Customers explicitly opt in to anything broader.
* **Privacy embedded into design.** Privacy controls are part of the feature's architecture, not bolted on at the end of a release.
* **Full functionality, positive-sum not zero-sum.** Security, functionality, and privacy coexist. We do not treat them as trade-offs to be negotiated away.
* **End-to-end security across the full lifecycle.** Data is encrypted in transit and at rest, access is logged, and deletion is supported across all storage tiers.
* **Visibility and transparency.** Privacy practices are documented and verifiable. This page is part of that commitment.
* **Respect for user privacy, kept user-centric.** Customers can see what's stored about them, export it, and request deletion.
***
## Environment segregation
Production, staging, and development environments are logically and (where possible) physically segregated. Each has its own:
* Database instance with separate credentials
* Secret store namespace
* Deployment target
* Telemetry stream
Customer Confidential data is not used in development or testing environments without explicit, written authorization from both the data owner and the CTO. Anonymized or synthetic fixtures are used by default.
***
## Application vulnerability management
* **Static analysis.** Linters, type checking, and dependency vulnerability scanners run in CI on every pull request. High-severity findings block merge.
* **Dependency review.** Third-party libraries are reviewed before adoption and tracked via lockfiles. Automated alerts surface newly disclosed vulnerabilities.
* **Patch timeline.** Patches that materially impact security are deployed within 90 days of discovery. Critical vulnerabilities are escalated and patched on a much shorter timeline.
***
## System security and acceptance testing
Security functionality is tested throughout the development lifecycle, not only at release:
* Unit and integration tests cover authentication, authorization, and data-handling paths.
* End-to-end tests cover critical user journeys (signup, payment, calendar sync, call placement).
* A release checklist is completed before any production deploy, documenting test results and any remediation activities.
When operating platforms or upstream services change (framework upgrades, runtime upgrades, telephony provider changes), affected business-critical applications are reviewed and re-tested before the change reaches production.
***
## Test data protection
Test data is selected carefully and protected at the level appropriate to its sensitivity. Customer Confidential data is not used for testing without the explicit permission of the data owner and the CTO. When customer data is required for incident reproduction, access is scoped, logged, and time-limited.
***
## Restrictions on changes to third-party software
Modifications to third-party software packages are discouraged. Where unavoidable, changes are limited to the minimum required, isolated in vendored forks, and reviewed under the same procedure as first-party code.
***
## Developer training
Software developers receive secure development training at least annually. Training content is determined by the CTO and addresses, at minimum:
* Authorization bypass prevention
* Secure session ID handling
* Injection attack prevention (SQL, command, template)
* Cross-site scripting (XSS) prevention
* Cross-site request forgery (CSRF) prevention
* Identification and avoidance of vulnerable libraries
***
## Exceptions and enforcement
Requests for exceptions to the Secure Development Policy must be submitted to the CTO for approval. Violations should be reported to the CTO and may result in immediate withdrawal of system access and disciplinary action up to and including termination.
***
## Related
* [Code Review Process](/policies/code-review-process): how changes are reviewed and merged.
* [Privacy Policy](https://revdesk.com/privacy)
* [Terms of Service](https://revdesk.com/terms)
# Privacy Policy
Source: https://docs.revdesk.com/policies/privacy-policy
How RevDesk (Cell Labs, Inc.) collects, uses, and protects your data.
The full, legally canonical Privacy Policy is published at **revdesk.com/privacy**.
Opens revdesk.com/privacy in a new tab
***
## Quick summary
* We collect only the data we need to run RevDesk for you: account details, billing information, call metadata, transcripts and recordings of calls placed through the platform, and integration data you've authorized us to access.
* We use that data to provide the service, secure your account, prevent fraud and abuse, and comply with our legal obligations.
* We don't sell your data. Your data and conversations are never used to train our models or anyone else's.
* You can export your data and request deletion from inside the app, or by writing to **[privacy@revdesk.com](mailto:privacy@revdesk.com)**.
This summary is non-binding. For the binding text, scope, and your rights under specific laws (GDPR, CCPA, etc.), see the canonical policy above.
***
## Related
* [Terms of Service](/policies/terms-of-service)
* [Engineering Documentation](/policies/engineering-documentation): security and privacy principles applied in our codebase
* [Code Review Process](/policies/code-review-process)
# Security
Source: https://docs.revdesk.com/policies/security
Encryption posture, sub-processors, access controls, retention, and residency. The full picture for security and compliance reviews.
# Security at RevDesk
RevDesk runs revenue conversations for regulated and high-trust businesses. The platform is built on encryption-everywhere defaults, named sub-processors with executed agreements, scoped access controls, and a documented audit trail. This page is the public reference. The detailed packet is available under NDA via sales.
SOC 2 mapping, sub-processor BAAs, security assessment summary, DPA, incident response runbook.
## Encryption
| Layer | Posture |
| - | - |
| In transit | TLS 1.2 or higher everywhere, TLS 1.3 on every public web surface. SRTP on real-time media. |
| At rest | AES-256 for every data store: Postgres, Vercel Blob (recordings, LOAs), backups. |
| Application secrets | Encrypted with `REVDESK_ENCRYPTION_KEY` before persistence. Provider keys never appear in plaintext logs. |
| Recordings | Encrypted at rest. Signed URLs expire on access. Retention default 90 days, tightened to 30 days under HIPAA mode; configurable per number. |
## Access controls
* **SSO and SAML** on RevDesk Custom. Users are provisioned into the organization on first successful SSO login. RevDesk does not offer SCIM or IdP directory sync, so deprovisioning is performed in RevDesk or by revoking access at the IdP.
* **Row-level scoping.** Every query is scoped to the signed-in user's organization.
* **Audit log.** Settings changes are audit-logged. Exportable on request.
* **Outreach disclosure attestation.** Every outbound call and SMS persists whether a recording disclosure or STOP-instructions phrase was attached at send time, plus a two-party-consent state flag for calls. Exportable via the Compliance Center on the Outreach page. See [Outreach audit](/concepts/outreach-audit).
* **Sub-entity isolation** for multi-tenant operators. See [Sub-entities](/concepts/sub-entities).
## Sub-processors
The authoritative, versioned list — every sub-processor, what each receives, and where it processes — is published at [revdesk.com/subprocessors](https://www.revdesk.com/subprocessors) and is incorporated into Annex III of your DPA. We give 30 days' notice before a new sub-processor begins processing, with a right to object. Highlights:
| Service | Role | BAA / DPA |
| - | - | - |
| Telnyx | Voice and SMS termination, branded calling, SIP trunking | BAA in place for HIPAA-enabled workspaces. DPA on file. |
| Hiya | Number reputation analytics (via Telnyx) | DPA. Verified per deployment for HIPAA-strict customers. |
| LiveKit Cloud | Real-time media and agent runtime for inbound, outbound, and browser-bridged AI calls (see Media path below) | BAA in place for HIPAA-enabled workspaces. |
| Deepgram | Answer classification on outbound calls (person vs voicemail) | BAA in place. Not in the conversation path, and recordings are not transcribed after the call. |
| OpenAI | Realtime speech-to-speech voice model (default); every text feature — assistant, drafting, summaries, embeddings; internal tooling | BAA in place for HIPAA-enabled workspaces. |
| Vercel | Application hosting and storage (Blob) | BAA available on Enterprise. |
| Neon (managed Postgres) | Primary data store | BAA available; encryption and backup defaults documented. |
| Stripe | Billing | BAA available. PHI never appears in invoice metadata. |
| Resend, PostHog, Sentry, Upstash, Trigger.dev, Bytemine | Email delivery, product analytics, error monitoring, caching, job orchestration, contact enrichment | See the full list for scope and DPA status. |
## Media path
Voice agents run as containers on the LiveKit stack, and where that stack runs differs by call direction. It is worth understanding, because it determines which parties touch call audio.
| Call type | Media runs on today | Who is in the audio path |
| - | - | - |
| Inbound AI calls | LiveKit Cloud | Telnyx for the PSTN leg, LiveKit Cloud for media and the agent runtime |
| Outbound AI calls | LiveKit Cloud | Telnyx for the PSTN leg, LiveKit Cloud for media and the agent runtime |
| Browser-bridged calls (augmented dialer) | LiveKit Cloud | Telnyx for the PSTN leg, plus LiveKit Cloud to join the operator's WebRTC leg to the same session |
We run LiveKit in two places: LiveKit Cloud, and a LiveKit stack our carrier operates inside its own network. Which one serves a given direction is a routing setting we move for reliability, so both are covered by BAA and DPA and both appear on the [sub-processor list](https://www.revdesk.com/subprocessors). Ordinary AI calls in both directions are served by LiveKit Cloud today; browser-bridged calls always are, which is inherent to that feature rather than a routing choice.
## Data residency
US default. Recordings, transcripts, and call metadata are stored in US regions. EU residency is available on RevDesk Custom on a customer-by-customer basis. Talk to sales if your contract requires a specific region.
## Retention
| Object | Default retention | Override |
| - | - | - |
| Call recordings | 90 days. 30 days under HIPAA mode. | Customer-configurable per number, 7 days to 1 year, or off |
| Transcripts | Matches recording retention | Customer-configurable |
| Call metadata | Retained for the life of the workspace (needed for billing, analytics, audit) | Reduced on contract request |
| Audit log | Retained for the life of the workspace | Exportable on offboarding |
An automated retention job purges expired audio and transcripts and writes an entry to the data-deletion audit log for every run. Customer-initiated deletion is supported in-app and via the API, and removes the record along with its content. On account closure, deletion completes within 90 days, matching the DPA.
Stored files (recordings, voicemail audio, uploads) are removed from object storage at the point of deletion. Deleted database rows remain recoverable only within our managed Postgres change-history window, which is measured in days and never exceeds 30; past that the history is discarded permanently. That history is not restored into production to recover deleted customer data.
## Network controls
* IP allowlisting on the API for RevDesk Custom.
* Branded calling at the carrier level with STIR/SHAKEN attestation. See [Caller trust](/concepts/caller-trust).
* DNC scrubbing and TCPA-aware sequencing run before every campaign dial leaves the platform: federal and org-level DNC lists, per-channel consent state, calling-window enforcement, and frequency caps are all evaluated per contact at dial time. Manually initiated dials from the dialer are not gated by campaign compliance rules, so the operator is responsible for them.
## SOC 2
SOC 2 Type II audit is in progress. Controls are mapped, evidence collection is underway, and the report will be available under NDA via sales when issued. Until then, the security packet covers the same control families and includes a gap analysis prepared by our auditors.
## HIPAA
PHI handling is gated by a per-workspace `hipaa_enabled` flag that activates only after you sign a BAA with RevDesk. The flag restricts LLM routing to BAA-covered providers (an ineligible selection is swapped to a covered default), strips recordings and transcripts from outbound webhooks and the public API, defaults recording retention to 30 days across the workspace's numbers, and emits a compliance audit log.
HIPAA mode is a **compliance boundary, not a storage switch**. It guarantees every provider touching call content is BAA-covered and tightens the retention default; whether call content is recorded and retained at all remains the workspace's own decision via its recording and transcript settings. See [HIPAA and BAA coverage](/concepts/hipaa) for the full posture and how to request a BAA.
## Incident response
Customers are notified without undue delay, and in any event within 72 hours, of a confirmed material incident affecting data we process on their behalf — the commitment in § 7 of the DPA. Under HIPAA, affected covered entities are notified within 60 days of discovery per § 164.410. Where we are the controller, supervisory authorities are notified within 72 hours per GDPR Article 33. Post-incident reports include root cause, remediation, and prevention measures.
For unconfirmed events, the customer security contact on file is notified during the investigation, not after.
## Reach security
* General security questions: [security@revdesk.com](mailto:security@revdesk.com)
* Compliance and BAAs: [compliance@revdesk.com](mailto:compliance@revdesk.com)
* Vulnerability disclosure: [security@revdesk.com](mailto:security@revdesk.com) (PGP key in the packet)
Sent under NDA. Includes mapping to SOC 2, DPA, BAA addendums, and the latest security assessment summary.
# Terms of Service
Source: https://docs.revdesk.com/policies/terms-of-service
The agreement that governs your use of RevDesk (Cell Labs, Inc.).
The full, legally canonical Terms of Service are published at **revdesk.com/terms**.
Opens revdesk.com/terms in a new tab
***
## Quick summary
* Using RevDesk means agreeing to the terms published at the canonical URL above.
* You're responsible for your use of the service, including the calls you make, the consent you've collected from the people you contact, and compliance with applicable law (TCPA, DNC, state calling rules, and so on).
* We provide the platform "as-is" with the warranties, limitations, and indemnities described in the canonical terms.
* Billing, refund, and cancellation rules are described in the canonical terms and reflected in your subscription settings inside the app.
This summary is non-binding. For the binding text, see the canonical terms above.
***
## Related
* [Privacy Policy](/policies/privacy-policy)
* [Engineering Documentation](/policies/engineering-documentation)
* [Code Review Process](/policies/code-review-process)