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 username and password (and otp if the user has two factor authentication enabled). This is the same credential a user logs into the portal with.

API token and secret:

Supply token and secret from 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 as Bearer in the Authorization header.

Errors

Errors are returned as a JSON object with an error message.

Status

Body

Meaning

400

{"error": "Missing required credentials"}

Neither username nor token and secret were supplied.

401

{"error": "Not Authenticated"}

The username or password is wrong.

401

{"error": "Invalid API token or secret"}

The API token or secret is wrong, the key was deleted, or the account is inactive.

423

{"error": "OTP Code is required"}

The user has two factor authentication enabled and otp was missing or wrong.

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

GET /myphones/api_key

List your keys. Secrets are never returned.

POST /myphones/api_key

Create a key. Body: {"name": "EHR integration"}

DELETE /myphones/api_key/{token}

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.

Authorization Header

Every request other than the initial token request and the refresh request MUST include the access token in the Authorization header using the Bearer scheme:

Authorization: Bearer <ACCESS_TOKEN>

Requests made without a valid token, or with an expired token, are rejected with a 401 response:

{
  "errors" : ["Not Authenticated"]
}

To check a token, or to see the user it belongs to, call GET /auth/token with the Authorization header. It returns the token’s claims:

{
  "username" : "jsmith",
  "user_id" : 1042,
  "domain" : "customer.ringrx.com",
  "backend" : "https://engine01.ringrx.com:8443",
  "role" : "admin",
  "exp" : 1792605000,
  "sub" : "Portal"
}

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

GET /pbxcallbacks

List the account’s callbacks.

GET /pbxcallbacks/types

List the callback types ({"fax": "New Fax", "voicemail": "New Voicemail", ...}).

GET /pbxcallbacks/{id}

Show one callback.

POST /pbxcallbacks

Create a callback.

PATCH /pbxcallbacks

Update a callback. The id goes in the body.

DELETE /pbxcallbacks/{id}

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

PATCH /phonenumbers

voicemail

Mailbox

PATCH /pbxmailboxes

fax

Fax Service

PATCH /pbxfaxes

call_alert

Group

PATCH /pbxgroups

outreach

Outreach Profile

PATCH /outreachprofiles/{id}

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 2xx or 3xx response 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.

  • cdr and recording callbacks 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, a User-Agent set 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 2026-10-21 17:45:10 UTC)

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 2026-10-21 17:45:10 UTC)

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: Bearer access 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