API
RingRx exposes all functions via API.
Swagger
We have a robust REST api documented in Swagger
All requests go to your portal host (for example https://<your-portal-host>). Parameters can be sent in the query string or as a JSON body with Content-Type: application/json. The examples below use a JSON body.
Authentication
We make use of JWT Authentication. This means that all requests except an initial login MUST bear an authorization token.
Access tokens are short lived (5 minutes). Make sure whatever library you are working with is prepared to fetch new tokens as needed.
Obtaining a Token
Authentication is performed against the POST /auth/token endpoint. Two credential types are supported. Both return a token data object on success.
- Username and password:
Supply
usernameandpassword(andotpif the user has two factor authentication enabled). This is the same credential a user logs into the portal with.- API token and secret:
Supply
tokenandsecretfrom an API key. This is the preferred method for integrations and automation. See API Keys for how to create one.
Username and password request:
curl -X POST "https://<your-portal-host>/auth/token" \
-H "Content-Type: application/json" \
-d '{"username": "jsmith@customer.ringrx.com", "password": "<PASSWORD>", "otp": "123456"}'
Leave out otp when the user does not have two factor authentication enabled.
A successful response returns a token data object:
{
"token_type" : "bearer",
"access_token" : "eyJhbGciOiJSUzI1NiJ9...",
"expires_in" : 1792605000,
"refresh_token" : "eyJhbGciOiJSUzI1NiJ9..."
}
- access_token:
The JWT to present on all subsequent requests. It is valid for 5 minutes.
- refresh_token:
A longer lived token used to obtain a new access token when the current one expires. Only returned for username and password logins. See Refreshing a Token.
- expires_in:
The time the access token expires, as a Unix timestamp in seconds (not a duration). Refresh shortly before this time.
- token_type:
The token scheme. Always
bearer; send it asBearerin theAuthorizationheader.
Errors
Errors are returned as a JSON object with an error message.
Status |
Body |
Meaning |
|---|---|---|
400 |
|
Neither |
401 |
|
The username or password is wrong. |
401 |
|
The API token or secret is wrong, the key was deleted, or the account is inactive. |
423 |
|
The user has two factor authentication enabled and |
Repeated failures lock the login out for a period of time. Successful and failed logins are recorded in the Audit Logs.
Two Factor Authentication
If the user receives one-time passcodes by text or email rather than an authenticator app, request a code before logging in:
curl -X POST "https://<your-portal-host>/auth/request_otp" \
-H "Content-Type: application/json" \
-d '{"username": "jsmith@customer.ringrx.com", "password": "<PASSWORD>"}'
Then call POST /auth/token with the code in otp. For unattended use, an API key avoids the passcode step entirely.
Using API Keys
API keys are the recommended way to authenticate non-interactive integrations. Each key consists of a token and a secret generated under a user’s account, and the resulting access token inherits that user’s role and permissions. See API Keys for details on creating and managing keys in the portal.
Keys can also be managed through the API, while logged in as the user who owns them:
Request |
Purpose |
|---|---|
|
List your keys. Secrets are never returned. |
|
Create a key. Body: |
|
Delete a key. |
Creating a key returns the secret once:
{
"name" : "EHR integration",
"token" : "jsmith_Xk29QpL0aZ7mN4rT8vWc",
"secret" : "a8F3kQ9zLm2Rt7Vx4Nc6Bp1Hs5Jd0Ye"
}
The user is emailed whenever a key is created or deleted.
To authenticate with an API key, exchange the token and secret for an access token:
curl -X POST "https://<your-portal-host>/auth/token" \
-H "Content-Type: application/json" \
-d '{"token": "<TOKEN>", "secret": "<SECRET>"}'
The response has the same shape as above, without a refresh_token:
{
"token_type" : "bearer",
"access_token" : "eyJhbGciOiJSUzI1NiJ9...",
"expires_in" : 1792605000
}
When the access token expires, request a new one with the token and secret again. Take the access_token from the response and present it as a bearer token on every subsequent request:
curl "https://<your-portal-host>/myphones" \
-H "Authorization: Bearer <ACCESS_TOKEN>"
Note
Unlike the username/password flow, API token authentication is never subject to a one-time passcode (OTP) challenge, which makes it suitable for unattended use.
Important
Treat the secret like a password. The secret is only shown once when the key is created and cannot be retrieved afterwards. If a key is compromised, delete it (DELETE /myphones/api_key/{token}) and issue a new one. Deleting a key stops it from issuing new access tokens at once. Access tokens already issued from it stay valid until they expire, at most 5 minutes later.
Refreshing a Token
Access tokens are short lived. After a username and password login, rather than re-submitting credentials, exchange the refresh_token for a fresh token data object using POST /auth/refresh:
curl -X POST "https://<your-portal-host>/auth/refresh" \
-H "Content-Type: application/json" \
-d '{"refresh_token": "<REFRESH_TOKEN>"}'
A successful refresh returns a new token data object, including a new refresh_token. The refresh token lasts for the account’s session length (30 minutes by default). Your client should refresh before expires_in passes, or detect a 401 and refresh before retrying the original request. If the refresh token is itself invalid or expired, the call returns 401 with {"error": "Not Authenticated"}; re-authenticate from the beginning via POST /auth/token.
API key logins do not get a refresh token. Request a new access token with the token and secret instead.
Full request and response schemas for all of the above are documented in Swagger
Callbacks
RingRx supports a robust callback system to allow you to build out your own integrations based upon receiving events from us. Callbacks can be configured from our existing REST api as documented in Swagger
Each callback is completely configurable with token replacements from the object being sent
Managing Callbacks
Callbacks belong to the account and can only be managed by admin users.
Request |
Purpose |
|---|---|
|
List the account’s callbacks. |
|
List the callback types ( |
|
Show one callback. |
|
Create a callback. |
|
Update a callback. The |
|
Delete a callback. |
Create request:
curl -X POST "https://<your-portal-host>/pbxcallbacks" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d @callback.json
{
"pbx_callback" : {
"name" : "EHR voicemail import",
"callback_type" : "voicemail",
"url" : "https://ehr.example.com/ringrx/voicemail?mailbox={mailbox}&caller={caller}",
"use_basic_auth" : true,
"auth_username" : "ringrx",
"auth_password" : "<PASSWORD>",
"custom_headers" : "X-Api-Key:abc123\r\nX-Source:ringrx"
}
}
The response is the saved callback, including its id. To update it, send the same object with its id to PATCH /pbxcallbacks.
Binding a Callback
Account wide types (cdr and recording) start firing as soon as they are created. The other types fire only once they are bound to an object, by setting the object’s pbx_callback_id to the callback’s id. Each object holds one callback; one callback can be bound to many objects.
Type |
Bound to |
Endpoint |
|---|---|---|
sms |
Phone Number |
|
voicemail |
Mailbox |
|
fax |
Fax Service |
|
call_alert |
Group |
|
outreach |
Outreach Profile |
|
cdr, recording |
The whole account |
(none) |
Read the object first, set pbx_callback_id, and send the whole object back, so other fields are not cleared. Set pbx_callback_id to null to unbind.
Name
This is a descriptive name for the callback and will be used inside audit logs.
Callback Type
This is what kind of callback is being configured (these are described in more detail below)
- sms:
This configures a callback that can be bound to a Phone Number. When that phone number receives an SMS, or a user sends an SMS from it, this callback is triggered. This type of callback can be bound to multiple phone numbers
- voicemail:
This configures a callback that can be bound to a mailbox. When a voicemail (only voice not fax) is deposited in that mailbox this callback is triggered. This type of callback can be bound to multiple mailboxes
- fax:
This configures a callback that can be bound to a fax service. When a fax is received, this callback is triggered. This type of callback can be bound to multiple fax services.
- cdr:
This callback is global. It will generate an event for EVERY call that is completed by the customer. Take care not to configure many of these.
- recording:
This callback is global. It will generate an event for EVERY call that has a recording attached to it. Take care not to configure many of these.
- outreach:
This configures a callback subscription to a specific outreach profile. The destination will receive updates to all events attached to that subscription including creation, updates and dispositioning of outreach events.
- call_alert:
This configures a callback subscription to real-time call events passing through a configured group.
URL
This is the URL destination of the callback. It must start with http:// or https://. This field MAY contain tokens in the URL or as part of the parameters.
Basic Auth
If “Use Basic Auth” is set to true then we will send the username and password configured in an Authorization: Basic header.
- use_basic_auth:
Boolean
- auth_username:
the basic auth username to be transmitted. This field is NOT token replaced
- auth_password:
the basic auth password to be transmitted. This field is NOT token replaced
Custom Headers
You can add any custom headers to the request you wish. These headers will be token replaced.
Headers are parsed one per line (\r\n between) with header and value split by a colon :.
- Example:
AuthToken:DESIGN-thick-because-guard
Write each header as Name:value with no space after the colon; any space is sent as part of the value. The value cannot itself contain a colon.
Note
Custom headers can be a great way to validate and authenticate inbound callback requests
Delivery and Retries
Every callback is an HTTP POST to the configured URL.
Any
2xxor3xxresponse counts as delivered.Any other response, a timeout, or a connection error is retried up to 3 more times: 5, 10 and 15 minutes after the previous attempt. After the fourth failed attempt the event is dropped.
cdrandrecordingcallbacks are first sent about 5 minutes after the call ends or the recording is stored. The other types are sent right away.Requests carry
Content-Type(application/json, or the file type for voicemail and fax) and, if configured, aUser-Agentset by RingRx.For voicemail and fax callbacks the result of each attempt (URL and HTTP status) is written to the message’s history.
Your endpoint should answer quickly with a 2xx and do any slow work afterwards. Because of retries, the same event can arrive more than once: use uuid or id to discard duplicates.
Token Replacement
To allow for maximum customization of callback events to your system each event type can be configured with an array of tokens that will be replaced by values from the event. This should be used in the URL and headers to customize what you receive. Tokens are not replaced in the body.
- Voicemail example:
https://some.url.com/voicemail?accountID=some_account&calling_number={caller}&filename={filename}&recipient={called}&mailbox={mailbox}
In the above example
- {caller}:
Will be replaced by the calling number
- {called}:
Will be replaced by the called number
- {filename}:
Will be replaced by the actual transmitted file name
- {mailbox}:
Will be replaced by the mailbox that collected this message
Values are inserted as they are, without URL encoding. Some values contain spaces or other characters that are not valid in a URL (for example {created_at} on voicemail and fax, or {caller_id_name}), so prefer reading those from the body or a header. A token with no value for an event is left in place unchanged.
SMS
SMS Callbacks are configured on phone number objects. They execute for inbound messages delivered to that number, and for outbound messages a user sends from that number.
For SENDING SMS messages consult the Swagger documentation at Swagger
SMS Callbacks are sent a JSON Post messages
- METHOD:
POST
- BODY:
JSON body as below
Inbound message:
{
"from" : "18185551234",
"to" : "18185559876",
"message" : "Test callback",
"timestamp" : "1792604934123",
"uuid" : "6ed8654e-a9d1-4a7c-9502-581210d0985f"
}
- from:
The sending number, as 11 digits with country code. For an outbound message this is the sending user’s address instead (for example
jsmith@customer.ringrx.com).- to:
The receiving number, as 11 digits with country code. A message to several recipients produces one callback per recipient.
- message:
The text body of the message.
- timestamp:
When RingRx received the message, in milliseconds since the Unix epoch (as a string).
- uuid:
The unique identifier of the message.
Inbound messages that are dropped or sent to spam by a text message rule, and replies to outreach reminders, do not fire the callback. Automatic replies sent by RingRx do not fire it either.
Available tokens are:
Token |
Description |
|---|---|
{from} |
The number sending the SMS message |
{to} |
The number receiving the SMS message |
{timestamp} |
When the message was received, in epoch milliseconds |
{length} |
The number of characters in the message |
The message text is not available as a token; read it from the body.
Voicemail
Voicemail Callbacks are sent a POST message with the file in the body
- METHOD:
POST
- BODY:
voicemail file (raw bytes, not multipart)
- CONTENT-TYPE:
mime type of the body payload
Example request, with the URL from the token example above:
POST /voicemail?accountID=some_account&calling_number=18185551234&filename=3f0c9a52-6d1e-4b8a-9c77-2f5e8d1b4a10.wav&recipient=18885439876&mailbox=1001 HTTP/1.1
Host: some.url.com
Content-Type: audio/x-wav
Content-Length: 182444
Authorization: Basic cmluZ3J4OnNlY3JldA==
AuthToken: DESIGN-thick-because-guard
<audio bytes>
Only voicemail is sent to this callback. Faxes received into the same mailbox are not.
Token |
Description |
|---|---|
{id} |
The unique identifier of the message |
{caller} |
The calling number leaving the message |
{called} |
The number called prior to the call going to voicemail |
{duration} |
The duration of the message, in seconds |
{filename} |
The filename contained in the body |
{mailbox} |
The mailbox that the voicemail was deposited in |
{created_at} |
Date and Time in GMT the message was left
(for example |
Fax
Fax Callbacks are sent a POST message with the file in the body
- METHOD:
POST
- BODY:
fax file (raw bytes, not multipart), normally a PDF
- CONTENT-TYPE:
mime type of the body payload, normally
application/pdf
Example request:
POST /fax?from=18185551234&pages=3&file=9b1e7c40-2a6f-4f0e-8d3b-5c2a1e9f7d64.pdf HTTP/1.1
Host: some.url.com
Content-Type: application/pdf
Content-Length: 84211
<PDF bytes>
Token |
Description |
|---|---|
{id} |
The unique identifier of the fax |
{caller} |
The sending number transmitting the fax |
{called} |
The number the fax was sent to |
{pages} |
The page count of the fax |
{filename} |
The filename contained in the body |
{mailbox} |
The mailbox that the fax was delivered to |
{created_at} |
Date and Time in GMT the fax was received
(for example |
CDR
CDR Callbacks are configured globally and will execute for each and every call in the organization. One callback is sent per call, about 5 minutes after it ends.
CDR Callbacks are sent a JSON Post messages
- METHOD:
POST
- BODY:
JSON object of Call detail record
{
"id" : 48211907,
"domain_name" : "customer.ringrx.com",
"responsible_party" : "jsmith",
"aleg_uuid" : "0f6d2b8e-3c41-4a7e-9d55-1e2f3a4b5c6d",
"start_time" : "2026-10-21T17:45:10.000Z",
"hangup_cause" : "NORMAL_CLEARING",
"dialed_digits" : "18885439876",
"calling_party" : "18185551234",
"called_party" : "jsmith",
"caller_id_number" : "18185551234",
"caller_id_name" : "Dr Smith",
"duration" : 94,
"bill_duration" : 81,
"direction" : "inbound",
"mailbox" : null,
"aleg_holdtime" : 0.0,
"bleg_holdtime" : 12.4,
"tags" : "inbound,reception_queue"
}
Available tokens are:
Token |
Description |
|---|---|
{id} |
The unique id of the CDR |
{domain_name} |
The account domain |
{aleg_uuid} |
The unique identifier of the A leg of the call |
{responsible_party} |
Who “owns” this cdr |
{start_time} |
Timestamp of the beginning of the call (ISO 8601, UTC) |
{hangup_cause} |
What the final disposition of the call was |
{dialed_digits} |
Originally dialed digits (can change as call is routed) |
{calling_party} |
Originator of the call |
{called_party} |
The party that was called |
{caller_id_number} |
CallerID number as transmitted or received |
{caller_id_name} |
CallerID name as transmitted or received |
{duration} |
Call total duration in seconds (includes ringing) |
{bill_duration} |
Call duration in seconds from answer |
{direction} |
Call direction (inbound, outbound, internal) |
{mailbox} |
If the call went to voicemail, which mailbox |
{aleg_holdtime} |
If the a_leg was placed on hold, for how long (total, seconds) |
{bleg_holdtime} |
If the b_leg was placed on hold, for how long (total, seconds) |
{tags} |
Comma separated list of tags applied to the call |
Recording
Recording Callbacks are configured globally and will execute for each and every recorded call in the organization. They are sent about 5 minutes after the recording is stored.
Recording Callbacks are sent a JSON Post messages
- METHOD:
POST
- BODY:
JSON object of Call detail record with new recording
The body has the same fields as the CDR callback, plus:
- recording_size:
The size of the recording file in bytes (as a string).
- recording_url:
Where to download the recording. The download needs an
Authorization: Beareraccess token (see Authentication).
{
"id" : 48211907,
"domain_name" : "customer.ringrx.com",
"responsible_party" : "jsmith",
"aleg_uuid" : "0f6d2b8e-3c41-4a7e-9d55-1e2f3a4b5c6d",
"start_time" : "2026-10-21T17:45:10.000Z",
"hangup_cause" : "NORMAL_CLEARING",
"dialed_digits" : "18885439876",
"calling_party" : "18185551234",
"called_party" : "jsmith",
"caller_id_number" : "18185551234",
"caller_id_name" : "Dr Smith",
"duration" : 94,
"bill_duration" : 81,
"direction" : "inbound",
"mailbox" : null,
"aleg_holdtime" : 0.0,
"bleg_holdtime" : 12.4,
"tags" : "inbound,reception_queue",
"recording_size" : "1503232",
"recording_url" : "https://<your-portal-host>/cdrs/48211907/recording"
}
curl -o recording "https://<your-portal-host>/cdrs/48211907/recording" \
-H "Authorization: Bearer <ACCESS_TOKEN>"
Available tokens are the same as for CDR callbacks. recording_size and recording_url are not available as tokens.
Outreach
Outreach Callbacks are configured per-profile and will execute for all events attached to that profile.
A callback is sent every time an event is saved: when it is created, when its call, SMS or email state changes, when it is confirmed or cancelled, and each time a line is added to its event_log. Expect several callbacks per event, each carrying the full current state.
Outreach Callbacks are sent a JSON Post messages
- METHOD:
POST
- BODY:
JSON object of Outreach Event
{
"id" : 90233,
"name" : "Jane Doe",
"phonenumber" : "18185551234",
"email_address" : "jane.doe@example.com",
"call_state" : "successful",
"sms_state" : "pending",
"email_state" : "pending",
"confirmed" : true,
"cancelled" : false,
"reminder_date" : "2026-10-22T16:30:00.000Z",
"attempt" : 1,
"voice" : true,
"sms" : true,
"email" : false,
"event_log" : [
{ "timestamp" : "2026-10-21T09:00:04.512-07:00", "event" : "Call answered" },
{ "timestamp" : "2026-10-21T09:00:31.077-07:00", "event" : "Appointment confirmed" }
]
}
call_state, sms_state and email_state are each pending, successful or failed. event_log timestamps are in the account’s time zone.
Available tokens are:
Token |
Description |
|---|---|
{id} |
The unique id of the Event |
{name} |
The entered “Name” on the event |
{phonenumber} |
The phone number used for Voice|SMS |
{email_address} |
The Email used for Email events |
{call_state} |
The status of the voice path of the event |
{sms_state} |
The status of the sms path of the event |
{email_state} |
The status of the email path of the event |
{confirmed} |
Boolean. The recipient has confirmed this event |
{cancelled} |
Boolean. The recipient has cancelled this event |
{reminder_date} |
The Date and Time of the event |
{attempt} |
Which Voice attempt we are on |
{voice} |
Boolean. This event will be notified via Call |
{sms} |
Boolean. This event will be notified via SMS |
{email} |
Boolean. This event will be notified via Email |
{event_log} |
Array. A log of all the attempts and responses. Read it from the body; it is not useful in a URL. |
Call_Alert
Call_Alert Callbacks are configured per-group and will be sent for each call being handled by that group just prior to EACH time the group offers a call to a user. This may mean you receive multiple call backs for the same call as it progresses through sequential offerings to each user on a group.
Call_Alert Callbacks are sent a JSON Post messages
- METHOD:
POST
- BODY:
JSON object of Call_Alert Event
{
"uuid" : "5bab4386-14a6-46ea-b200-6e47a91837cb",
"created_at" : "1709075290020813",
"callback_id" : "99",
"domain_name" : "customer.ringrx.com",
"originally_dialed_number" : "+18885439876",
"dialed_number" : "8000",
"caller_number" : "+18185551234",
"caller_name" : "Dr Smith",
"tags" : "inbound,reception_queue",
"dialed_user_emails" : "ssmith@example.com,jjones@example.com",
"dialed_users" : "ssmith,jjones"
}
created_at is in microseconds since the Unix epoch. dialed_user_emails lists the email address of each dialed user that has one, in the same order as dialed_users; it is in the body only, not a token.
Available tokens are:
Token |
Description |
|---|---|
{uuid} |
The unique id of the call |
{created_at} |
When the call began, in microseconds since the Unix epoch |
{callback_id} |
The ID of the callback being executed |
{domain_name} |
The domain name of the customer |
{originally_dialed_number} |
The originally called number |
{dialed_number} |
The current dialed number/extension |
{caller_number} |
The caller id number of the caller |
{caller_name} |
The caller id name of the caller |
{tags} |
Comma separated list of tags applied to the call |
{dialed_users} |
Comma separated list of users or ext about to be dialed |