Overview
Authenticate with your API key and POST a date range to any export endpoint. Rilla returns a structured payload of analytics data you can load into a database, spreadsheet, or BI tool.
Base URL
https://customer.rillavoice.comAuthentication
Learn how to authenticate your requests with an API key.
Endpoints
Explore the available export endpoints.
Code Examples
Copy-paste examples in Python, JavaScript, and cURL.
How it works
Admins and Team Managers can create API keys in the Rilla web app. Follow Get an API key for the Settings location, team access, and one-time key reveal. If you don’t have access, ask your organization’s admin or your Rilla account manager for a key.
Include your API key in the Authorization header of every request: Authorization: YOUR_API_KEY.
POST a date range to /export/conversations, /export/teams, or /export/users.
The conversations endpoint is paginated. Use totalPages in the response to loop through all results.
Authentication
All requests to the Data Export API must include a valid API key passed in the Authorization header.
POST /export/conversations HTTP/1.1
Host: customer.rillavoice.com
Authorization: YOUR_API_KEY
Content-Type: application/jsonRequest Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | API key. Format: YOUR_API_KEY. |
Content-Type | string | Yes | Must be application/json. |
Obtaining an API Key
Open Settings → Developer and select Create API key in the API keys tab, or ask your organization’s admin or your Rilla account manager for a key. Follow the Quickstart instructions for the creation and one-time reveal steps. Each key belongs to a single Rilla organization. Admins can select organization-wide access or specific teams; Team Managers are limited to teams they manage. Data Export includes the selected teams and their subteams.
Rilla validates the API key and its organization mapping; successful authentication can be cached for up to five minutes. If the key is missing or unrecognized, the API returns 401 Unauthorized.
Keep your key secure
API keys grant access to your organization's data and can also be used with the Custom CRM API. Do not expose them in client-side code, public repositories, or logs. Contact support@rilla.com immediately if a key is ever compromised.
GET /export/conversations/{conversationId}
Fetches a single conversation by its ID. Returns the same conversation object as the list endpoint below, but as a bare object rather than inside a paginated wrapper.
Use this when you already know a conversation's ID — for example, a conversation_id received from a webhook — and want to pull its full export record without specifying a date range.
Request Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | API key. Format: YOUR_API_KEY. |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
conversationId | string (UUID) | Yes | ID of the conversation to fetch. Must be a valid UUID. |
Response
Returns a single ConversationsExport object. Unlike the list endpoint, there is no pagination wrapper.
Returns 404 Not Found if the conversation does not exist, or if it falls outside the scope of your API key. Team-scoped keys receive a 404 (rather than 403) for conversations they cannot access, so existence is never leaked.
Example Request
GET /export/conversations/f1a3e9d8-2b46-4c1a-9c7e-7e4a18d2c93f HTTP/1.1
Host: customer.rillavoice.com
Authorization: YOUR_API_KEYExample Response
Returns ConversationsExport. This example includes every documented field.
{
"conversationId": "11111111-1111-4111-8111-111111111111",
"recordingId": "22222222-2222-4222-8222-222222222222",
"date": "2026-09-01T14:00:00Z",
"processedDate": "2026-09-01T15:12:00Z",
"title": "Home estimate - Jordan Lee",
"duration": 3240,
"crmEventID": "evt-90817",
"user": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Alex Morgan",
"email": "alex@example.com"
},
"transcriptUrl": "https://example.com/temporary-transcript.json?signature=EXAMPLE",
"jobNumber": "JOB-4471",
"stLink": "https://go.servicetitan.com/jobs/4471",
"totalSold": 12500,
"outcome": "Sold",
"jobSummary": "Rep walked the customer through the premium package and scheduled install.",
"customSummary": "Customer expressed interest in premium package...",
"repSpeedWPM": 148,
"repTalkRatio": 0.54,
"longestRepMonologue": 120,
"longestCustomerMonologue": 64,
"totalComments": 3,
"rillaUrl": "https://app.rilla.com/conversations/single?id=11111111-1111-4111-8111-111111111111",
"audioUrl": "https://example.com/temporary-audio.mp3?signature=EXAMPLE",
"viewers": [
{
"userId": "user-014",
"name": "Manager Mike",
"viewCount": 2,
"totalViewTimeMs": 540000
}
],
"checklists": [
{
"name": "Discovery",
"checklistName": "Discovery",
"score": 1,
"checklistScore": 1,
"denominator": 1,
"checklistDenominator": 1,
"trackerData": [
{
"trackerId": "1042",
"name": "Asked about budget",
"trackerName": "Asked about budget",
"isHit": true,
"trackerIsHit": true,
"aiScore": 0.92,
"trackerAiScore": 0.92
}
]
}
],
"aiTrackers": [
{
"checklistName": "Discovery",
"trackerId": "1043",
"trackerName": "Mentioned financing",
"isHit": true,
"aiScore": 0.92
}
],
"customFields": {
"leadSource": "Website"
},
"customer": {
"name": "Jordan Lee",
"email": "jordan@example.com",
"phone": "+1-202-555-0142",
"address": {
"street": "123 Example Street",
"unit": null,
"city": "New York",
"state": "NY",
"zip": "10001",
"country": "US"
}
},
"surveyResults": [
{
"surveyId": "55555555-5555-4555-8555-555555555555",
"surveyName": "Discovery quality",
"data": {
"next_step_confirmed": true
}
}
]
}POST /export/conversations
Exports data for all conversations recorded during the provided time range. Results are paginated — use page and limit to iterate.
The response also includes recordings from the time range that have not (or not yet) produced a conversation: awaitingUpload for recordings still on the rep's device, and errored for recordings that were uploaded but did not produce a conversation. Both carry the same recording and job data as conversations, minus fields that only exist once a conversation has been processed (talk metrics, trackers, summaries, audio/transcript URLs).
Request Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | API key. Format: YOUR_API_KEY. |
Content-Type | string | Yes | Must be application/json. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
fromDate | string | Yes | Beginning of the time range; use a UTC timestamp ending in Z. |
toDate | string | Yes | End of the time range; use a UTC timestamp ending in Z. Conversation bounds are inclusive. Most user/team metrics are inclusive too, except scorecards/live coaching which exclude the end. Choose an end after the start. |
dateType | string | No | timeOfRecording (default) or processedDate. |
page | number | No | 1-based page index. Defaults to 1. |
limit | number | No | Conversations per page. Max 50. Defaults to 50. |
users | string[] | No | Exact, case-sensitive user emails. Omit or send [] for all users. Team-scoped filters with no allowed matches currently fall back to all allowed users; verify returned emails. |
Response
| Field | Type | Description |
|---|---|---|
currentPage | number | The current page number. |
totalPages | number | Total number of pages, across the conversations, awaitingUpload, and errored arrays. |
totalConversations | number | Total conversations for the date range. |
totalAwaitingUpload | number | Total recordings awaiting upload for the date range. Omitted when dateType is processedDate. |
totalErrored | number | Total errored recordings for the date range. |
conversations | array | Conversation objects. See ConversationsExport. |
awaitingUpload | array | Recordings not yet uploaded from the rep's device. Omitted when dateType is processedDate, since these recordings have not been processed. See RecordingsExport. |
errored | array | Recordings that were uploaded but did not produce a conversation. See ErroredRecordingsExport. |
All three arrays are paginated independently by the same page and limit — page 2 returns the second page of conversations and the second page of each recordings array. totalPages covers whichever array has the most pages, so pages near the end may return empty arrays for the sets that are already exhausted.
Example Request
{
"fromDate": "2026-09-01T00:00:00Z",
"toDate": "2026-09-02T00:00:00Z",
"dateType": "timeOfRecording",
"page": 1,
"limit": 50
}Example Response
Returns ConversationsExportResponse. This example includes every documented field.
{
"currentPage": 1,
"totalPages": 1,
"totalConversations": 1,
"totalAwaitingUpload": 1,
"totalErrored": 1,
"conversations": [
{
"conversationId": "11111111-1111-4111-8111-111111111111",
"recordingId": "22222222-2222-4222-8222-222222222222",
"date": "2026-09-01T14:00:00Z",
"processedDate": "2026-09-01T15:12:00Z",
"title": "Home estimate - Jordan Lee",
"duration": 3240,
"crmEventID": "evt-90817",
"user": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Alex Morgan",
"email": "alex@example.com"
},
"transcriptUrl": "https://example.com/temporary-transcript.json?signature=EXAMPLE",
"jobNumber": "JOB-4471",
"stLink": "https://go.servicetitan.com/jobs/4471",
"totalSold": 12500,
"outcome": "Sold",
"jobSummary": "Rep walked the customer through the premium package and scheduled install.",
"customSummary": "Customer expressed interest in premium package...",
"repSpeedWPM": 148,
"repTalkRatio": 0.54,
"longestRepMonologue": 120,
"longestCustomerMonologue": 64,
"totalComments": 3,
"rillaUrl": "https://app.rilla.com/conversations/single?id=11111111-1111-4111-8111-111111111111",
"audioUrl": "https://example.com/temporary-audio.mp3?signature=EXAMPLE",
"viewers": [
{
"userId": "user-014",
"name": "Manager Mike",
"viewCount": 2,
"totalViewTimeMs": 540000
}
],
"checklists": [
{
"name": "Discovery",
"checklistName": "Discovery",
"score": 1,
"checklistScore": 1,
"denominator": 1,
"checklistDenominator": 1,
"trackerData": [
{
"trackerId": "1042",
"name": "Asked about budget",
"trackerName": "Asked about budget",
"isHit": true,
"trackerIsHit": true,
"aiScore": 0.92,
"trackerAiScore": 0.92
}
]
}
],
"aiTrackers": [
{
"checklistName": "Discovery",
"trackerId": "1043",
"trackerName": "Mentioned financing",
"isHit": true,
"aiScore": 0.92
}
],
"customFields": {
"leadSource": "Website"
},
"customer": {
"name": "Jordan Lee",
"email": "jordan@example.com",
"phone": "+1-202-555-0142",
"address": {
"street": "123 Example Street",
"unit": null,
"city": "New York",
"state": "NY",
"zip": "10001",
"country": "US"
}
},
"surveyResults": [
{
"surveyId": "55555555-5555-4555-8555-555555555555",
"surveyName": "Discovery quality",
"data": {
"next_step_confirmed": true
}
}
]
}
],
"awaitingUpload": [
{
"recordingId": "rec-2b91d3aa",
"date": "2026-09-01T17:45:00Z",
"title": "Panel Upgrade - Sarah Lee",
"duration": 1860,
"crmEventID": "evt-90932",
"user": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Alex Morgan",
"email": "alex@example.com"
},
"jobNumber": "JOB-4519",
"stLink": "https://go.servicetitan.com/jobs/4519",
"totalSold": null,
"outcome": null,
"customFields": {
"leadSource": "Website"
}
}
],
"errored": [
{
"recordingId": "rec-2b91d3aa",
"date": "2026-09-01T17:45:00Z",
"title": "Panel Upgrade - Sarah Lee",
"duration": 1860,
"crmEventID": "evt-90932",
"user": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Alex Morgan",
"email": "alex@example.com"
},
"jobNumber": "JOB-4519",
"stLink": "https://go.servicetitan.com/jobs/4519",
"totalSold": null,
"outcome": null,
"customFields": {
"leadSource": "Website"
},
"processedDate": "2026-09-01T10:02:00Z",
"error": "AUDIO_TOO_SHORT"
}
]
}POST /export/teams
Returns a TeamsExportResponse containing teams: TeamsExport[]. The response is not paginated.
Request Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | API key. Format: YOUR_API_KEY. |
Content-Type | string | Yes | Must be application/json. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
fromDate | string | Yes | Beginning of the time range; use a UTC timestamp ending in Z. |
toDate | string | Yes | End of the time range; use a UTC timestamp ending in Z. Conversation bounds are inclusive. Most user/team metrics are inclusive too, except scorecards/live coaching which exclude the end. Choose an end after the start. |
includeSubteams | boolean | No | Whether to roll descendant-team members into each team’s metrics. Defaults to false; does not remove descendant team rows. |
Response
| Field | Type | Description |
|---|---|---|
teams | array | Array of TeamsExport objects. |
Example Request
{
"fromDate": "2026-09-01T00:00:00Z",
"toDate": "2026-09-02T00:00:00Z",
"includeSubteams": true
}Example Response
Returns TeamsExportResponse. This example includes every documented field.
{
"teams": [
{
"name": "West Region",
"teamId": "team-42",
"externalTeamId": "ST-WEST",
"parentTeamId": null,
"parentTeamName": null,
"analyticsViewed": 12,
"appointmentsRecorded": 88,
"averageConversationDuration": 2980,
"averageConversationLength": 3120,
"clipCommentsGiven": 7,
"clipsCreated": 14,
"clipViewDuration": 86400,
"commentsGiven": 41,
"commentsRead": 33,
"commentsReceived": 52,
"conversationViewDuration": 172800,
"conversationsCommentedOn": 29,
"conversationsRecorded": 95,
"conversationsViewed": 140,
"longestCustomerMonologueAverage": 58,
"longestMonologueAverage": 102,
"patienceAverage": 0.71,
"recordingCompliance": 0.92,
"ridealongsReceived": 4,
"scorecardsGiven": 18,
"scorecardsReceived": 22,
"talkRatioAverage": 0.52,
"totalAppointments": 96,
"totalUsersWhoRecorded": 9,
"totalUsers": 11
}
]
}POST /export/users
Returns a UsersExportResponse containing users: UsersExport[]. Each user has all documented fields, including nested live coaching metrics. The response is not paginated.
Request Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | API key. Format: YOUR_API_KEY. |
Content-Type | string | Yes | Must be application/json. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
fromDate | string | Yes | Beginning of the time range; use a UTC timestamp ending in Z. |
toDate | string | Yes | End of the time range; use a UTC timestamp ending in Z. Conversation bounds are inclusive. Most user/team metrics are inclusive too, except scorecards/live coaching which exclude the end. Choose an end after the start. |
users | string[] | No | Exact, case-sensitive user emails. Omit or send [] for all users. Team-scoped filters with no allowed matches currently fall back to all allowed users; verify returned emails. |
Response
| Field | Type | Description |
|---|---|---|
users | array | Array of UsersExport objects. |
Example Request
{
"fromDate": "2026-09-01T00:00:00Z",
"toDate": "2026-09-02T00:00:00Z",
"users": ["alex@example.com"]
}Example Response
Returns UsersExportResponse. This example includes every documented field.
{
"users": [
{
"userId": "33333333-3333-4333-8333-333333333333",
"name": "Alex Morgan",
"email": "alex@example.com",
"accountSetUp": true,
"isRemoved": false,
"role": "Sales Rep",
"teams": [
{
"teamId": "44444444-4444-4444-8444-444444444444",
"name": "West region"
}
],
"timeOfFirstRecording": "2023-11-02T16:20:00Z",
"lastActive": "2026-09-01T09:45:00Z",
"hasVoiceId": true,
"analyticsViewed": 5,
"appointmentsRecorded": 46,
"averageConversationDuration": 3150,
"averageConversationLength": 3260,
"averageScriptCompliance": 0.81,
"averageTimePerRidealong": 1800,
"clipCommentsGiven": 3,
"clipsCreated": 6,
"clipViewDuration": 43200,
"commentsReceived": 28,
"commentsRead": 21,
"commentsGiven": 12,
"conversationsCommentedOn": 15,
"conversationsRecorded": 48,
"conversationsViewed": 70,
"conversationViewDuration": 86400,
"lastRidealongAt": "2026-09-01T13:00:00Z",
"longestMonologueAverage": 115,
"longestCustomerMonologueAverage": 52,
"patienceAverage": 0.68,
"percentageOfRepsWithCommentsGiven": null,
"recordingCompliance": 0.9,
"ridealongsReceived": 2,
"scorecardsReceived": 9,
"scorecardsGiven": 4,
"talkRatioAverage": 0.53,
"totalAppointments": 51,
"totalEndOfRidealongsReached": 1,
"totalRidealongsCompleted": 3,
"viewedRecordedRatio": 0.5,
"liveCoaching": {
"sessionsHosted": 2,
"viewEventsReceived": 18,
"uniqueViewers": 5,
"viewDurationMinutes": 64,
"commentsReceived": 7,
"uniqueCommenters": 3,
"moneySaved": 1200,
"lastCoachedAt": "2026-09-01T10:30:00Z"
}
}
]
}Data Models
ConversationsExport
One processed conversation, including linked CRM data, customer, coaching results and temporary download URLs. Also returned directly by GET /export/conversations/{conversationId}. Open ConversationsExport →
| Field | Type | Description |
|---|---|---|
conversationId | string | ID of the conversation. |
recordingId | string | ID of the underlying recording. |
date | string | Recording start timestamp in ISO 8601 UTC. List selection uses inclusive date bounds. |
processedDate | string | Date/time the conversation finished processing (ISO 8601). |
title | string | null | Title of the conversation / appointment. |
duration | number | Processed conversation duration in seconds. |
crmEventID | string | null | ID of the associated CRM / calendar event. |
user | RecordingUser | The rep who recorded the conversation: { id, name, email }. |
transcriptUrl | string | Temporary presigned S3 URL for the conversation's transcript. Expires 6 hours after the response is issued — download or persist the file rather than storing the URL. Temporary signing credentials may cause earlier expiry. |
jobNumber | string | null | Job number from the CRM / calendar event. |
stLink | string | null | ServiceTitan job link when the linked appointment uses that integration; null otherwise. Not a generic CRM URL. |
totalSold | number | null | Price on the linked appointment in its source currency, or null. This field alone does not establish a sold outcome; no currency code is returned. |
outcome | string | null | Outcome of the appointment. |
jobSummary | string | null | AI-generated summary of the job. |
customSummary | string | null | Admin summary / custom insights in your organization's format. |
repSpeedWPM | number | null | Rep speaking speed, in words per minute. |
repTalkRatio | number | null | Fraction of time the rep was talking (0–1). |
longestRepMonologue | number | null | Longest uninterrupted rep monologue, in seconds. |
longestCustomerMonologue | number | null | Longest uninterrupted customer monologue, in seconds. |
totalComments | number | All-time comments on this conversation, not bounded by the request window. |
rillaUrl | string | Link to view the conversation in the Rilla web app. |
audioUrl | string | Temporary presigned S3 URL for the conversation's audio recording. Expires 6 hours after the response is issued — download or persist the file rather than storing the URL. Temporary signing credentials may cause earlier expiry. |
viewers | Viewer[] | Users who viewed the conversation. See Viewer. |
checklists | Checklist[] | Coaching checklists scored for the conversation. Each checklist contains its trackers. See Checklist. |
aiTrackers | AiTracker[] | Trackers outside the checklist, from the latest completed evaluations. Not a flattened copy of checklists.trackerData. |
customFields | object | null | Custom fields from the calendar event. Keys and values are customer-defined. null if none are set. |
customer | ConversationCustomer | null | Linked customer contact and structured address; null when no customer fields exist. |
surveyResults | SurveyConversationResult[] | Survey results. Each result includes a survey ID, name and customer-defined data; the upstream service may include additional metadata. |
Viewer
One viewer of the conversation; totals include all-time views, not just views in the export window. Open Viewer →
| Field | Type | Description |
|---|---|---|
userId | string | Rilla user ID of the viewer. |
name | string | Name of the viewer. |
viewCount | number | Number of times this user viewed the conversation. |
totalViewTimeMs | number | Total time this user spent viewing, in milliseconds. |
Checklist
Latest completed evaluation per checklist for this conversation. Includes checklist trackers and compatibility aliases. Open Checklist →
| Field | Type | Description |
|---|---|---|
name | string | Alias of checklistName. |
checklistName | string | Name of the checklist. |
score | number | Alias of checklistScore. |
checklistScore | number | Number of trackers hit on this checklist. |
denominator | number | Alias of checklistDenominator. |
checklistDenominator | number | Total number of trackers in the checklist. |
trackerData | Tracker[] | The individual trackers evaluated for this checklist. See Tracker. |
Tracker
An in-checklist tracker. Prefer the tracker-prefixed fields; unprefixed fields are compatibility aliases with the same values. Open Tracker →
| Field | Type | Description |
|---|---|---|
trackerId | string | Unique identifier of the tracker. Stable across conversations, so it can be used to group or join tracker results. |
name | string | Alias of trackerName. |
trackerName | string | Name of the tracker. |
isHit | boolean | null | Alias of trackerIsHit. |
trackerIsHit | boolean | null | Whether the tracker was hit during the conversation. |
aiScore | number | null | Alias of trackerAiScore. |
trackerAiScore | number | null | AI-generated score for the tracker. |
AiTracker
A tracker evaluated outside a checklist (is_in_checklist = false). This array is not a flattened duplicate of checklists.trackerData. Open AiTracker →
| Field | Type | Description |
|---|---|---|
checklistName | string | Name of the checklist this tracker belongs to. |
trackerId | string | Unique identifier of the tracker. Stable across conversations, so it can be used to group or join tracker results. |
trackerName | string | Name of the tracker. |
isHit | boolean | null | Whether the tracker was hit during the conversation. |
aiScore | number | null | AI-generated score for the tracker. |
RecordingsExport
A recording awaiting upload. No conversation, transcript or coaching results exist yet. Open RecordingsExport →
| Field | Type | Description |
|---|---|---|
recordingId | string | ID of the recording. |
date | string | Date/time the recording was made (ISO 8601). |
title | string | null | Title of the recording / appointment. |
duration | number | Duration in seconds. |
crmEventID | string | null | ID of the associated CRM / calendar event. |
user | RecordingUser | The rep who made the recording: { id, name, email }. |
jobNumber | string | null | Job number from the CRM / calendar event. |
stLink | string | null | ServiceTitan job link when the linked appointment uses that integration; null otherwise. Not a generic CRM URL. |
totalSold | number | null | Price on the linked appointment in its source currency, or null. This field alone does not establish a sold outcome; no currency code is returned. |
outcome | string | null | Outcome of the appointment. null if no outcome is recorded. |
customFields | object | null | Custom fields from the calendar event. Keys and values are customer-defined. null if none are set. |
ErroredRecordingsExport
A recording that did not produce a conversation. Includes recording fields plus its processing timestamp and error. Open ErroredRecordingsExport →
| Field | Type | Description |
|---|---|---|
recordingId | string | ID of the recording. |
date | string | Date/time the recording was made (ISO 8601). |
title | string | null | Title of the recording / appointment. |
duration | number | Duration in seconds. |
crmEventID | string | null | ID of the associated CRM / calendar event. |
user | RecordingUser | The rep who made the recording: { id, name, email }. |
jobNumber | string | null | Job number from the CRM / calendar event. |
stLink | string | null | ServiceTitan job link when the linked appointment uses that integration; null otherwise. Not a generic CRM URL. |
totalSold | number | null | Price on the linked appointment in its source currency, or null. This field alone does not establish a sold outcome; no currency code is returned. |
outcome | string | null | Outcome of the appointment. null if no outcome is recorded. |
customFields | object | null | Custom fields from the calendar event. Keys and values are customer-defined. null if none are set. |
processedDate | string | null | Date/time processing finished (ISO 8601). |
error | string | Why the recording did not produce a conversation, e.g. AUDIO_TOO_SHORT or NO_TRANSCRIPT. |
Team
One team and its analytics. Membership uses current active, non-hidden users. includeSubteams controls descendant roll-up, not which team rows appear. Users can contribute to multiple teams; do not sum teams as an organization total. Open TeamsExport →
| Field | Type | Description |
|---|---|---|
name | string | Name of the team. |
teamId | string | Rilla team ID. |
externalTeamId | string | null | ID of the team in the connected CRM. null if not linked. |
parentTeamId | string | null | ID of the parent team. null for top-level teams. |
parentTeamName | string | null | Name of the parent team. null for top-level teams. |
analyticsViewed | number | Number of analytics views by team members. |
appointmentsRecorded | number | Appointments that were recorded. |
averageConversationDuration | number | null | Mean of each member’s mean recording duration, in seconds. Each member with data has equal weight; null if no member has data. |
averageConversationLength | number | null | Mean of each member’s mean rep + customer talk time, in seconds; excludes silence. Null without data. |
clipCommentsGiven | number | Comments given on clips. |
clipsCreated | number | Clips created. |
clipViewDuration | number | Total clip view duration, in seconds. |
commentsGiven | number | Comments given by team members. |
commentsRead | number | Comments read by team members. |
commentsReceived | number | Comments received by team members. |
conversationViewDuration | number | Total conversation view duration, in seconds. |
conversationsCommentedOn | number | Conversations that received at least one comment. |
conversationsRecorded | number | Distinct recordings made in the window, including recordings that have not produced a processed conversation. |
conversationsViewed | number | Conversations viewed by team members. |
longestCustomerMonologueAverage | number | null | Mean of members’ median longest customer monologues, in seconds; null without data. |
longestMonologueAverage | number | null | Mean of members’ median longest rep monologues, in seconds. Each member with data has equal weight; null without data. |
patienceAverage | number | null | Mean stored patience metric for conversations recorded in the window; null if no measurement exists. Do not treat it as a percentage. |
recordingCompliance | number | null | Eligible recorded appointments divided by eligible appointments, rounded to two decimals. Fraction, not a percentage: 0.92 means 92%. Null when there is no eligible denominator. |
ridealongsReceived | number | Ride-alongs received. |
scorecardsGiven | number | Scorecards given. |
scorecardsReceived | number | Scorecards received. |
talkRatioAverage | number | null | Mean rep talk ratio, rounded to two decimals. Fraction: 0.53 means 53%; null without measurements. |
totalAppointments | number | Eligible appointment-attendee rows with appointment end_time in the window and more than 3 hours ago. Compliance-disabled appointments/outcomes are excluded; calendar-only sources are excluded when a CRM integration exists. |
totalUsersWhoRecorded | number | Number of team members who recorded at least one conversation. |
totalUsers | number | Total number of users in the team. |
User
One user and their activity metrics. Returned inside users, not as the top-level response. Hidden users are excluded; removed users can still be returned. Identity, role, teams, first recording and lastActive reflect current/all-time data, not the export window. Open UsersExport →
| Field | Type | Description |
|---|---|---|
userId | string | Rilla user ID. |
name | string | null | Full name. May be null when a source name component is missing. |
email | string | Email address. |
accountSetUp | boolean | Whether the user has completed account setup. |
isRemoved | boolean | Whether the user has been removed/deactivated. |
role | string | null | Role display name, not a fixed enum. May be null if there is no matching role. |
teams | TeamMembership[] | null | Current team memberships, sorted by team name. Null (not an empty array) when the user has no team membership. |
timeOfFirstRecording | string | null | Timestamp of the user's first recording (ISO 8601), or null. |
lastActive | string | null | Timestamp of the user's last activity (ISO 8601), or null. For reps this is their most recent recording; for other roles, their most recent product activity. Not bounded by the requested date range. |
hasVoiceId | boolean | Whether the user has an enrolled voice ID. |
analyticsViewed | number | Number of analytics views. |
appointmentsRecorded | number | null | Appointments recorded. |
averageConversationDuration | number | null | Mean recording duration in seconds, including recordings without a processed conversation. Null when no duration is available. |
averageConversationLength | number | null | Mean rep talk time plus customer talk time in seconds for processed conversations. Excludes silence; null without measurements. |
averageScriptCompliance | number | null | Mean checklist score/denominator over completed checklist evaluations on recordings in the window, rounded to two decimals. Fraction; null without usable results. Multiple evaluations can contribute. |
averageTimePerRidealong | number | null | Average time per ride-along, in seconds. |
clipCommentsGiven | number | Comments given on clips. |
clipsCreated | number | Clips created. |
clipViewDuration | number | Total clip view duration, in seconds. |
commentsReceived | number | Comments received. |
commentsRead | number | Comments read. |
commentsGiven | number | Comments given. |
conversationsCommentedOn | number | Conversations the user commented on. |
conversationsRecorded | number | Distinct recordings made in the window, including recordings that have not produced a processed conversation. |
conversationsViewed | number | Conversations viewed. |
conversationViewDuration | number | Total conversation view duration, in seconds. |
lastRidealongAt | string | null | Most recent completed ride-along in the requested window; null if none. |
longestMonologueAverage | number | null | Median (not arithmetic mean) of the longest rep monologue per conversation, in seconds. The field name is retained for compatibility. |
longestCustomerMonologueAverage | number | null | Median of the longest customer monologue per conversation, in seconds. Null without measurements. |
patienceAverage | number | null | Mean stored patience metric for conversations recorded in the window; null if no measurement exists. Do not treat it as a percentage. |
percentageOfRepsWithCommentsGiven | number | null | Manager/admin comment reach divided by the relevant rep count, rounded to two decimals. Uses the conversation recording window, not comment creation time. Fraction (not multiplied by 100); null without an applicable denominator. |
recordingCompliance | number | null | Eligible recorded appointments divided by eligible appointments, rounded to two decimals. Fraction, not a percentage: 0.92 means 92%. Null when there is no eligible denominator. |
ridealongsReceived | number | Ride-alongs received. |
scorecardsReceived | number | Scorecards received. |
scorecardsGiven | number | Scorecards given. |
talkRatioAverage | number | null | Mean rep talk ratio, rounded to two decimals. Fraction: 0.53 means 53%; null without measurements. |
totalAppointments | number | Eligible appointment-attendee rows with appointment end_time in the window and more than 3 hours ago. Compliance-disabled appointments/outcomes are excluded; calendar-only sources are excluded when a CRM integration exists. |
totalEndOfRidealongsReached | number | Ride-alongs completed through to the end. |
totalRidealongsCompleted | number | Ride-alongs completed. |
viewedRecordedRatio | number | null | Self-viewed recorded conversations divided by distinct recordings made in the window, rounded to two decimals. Both recording and view must fall in the window. Can be null even when recordings exist but none were self-viewed; not conversationsViewed / conversationsRecorded. |
liveCoaching | LiveCoachingMetrics | Live coaching metrics. See LiveCoaching. |
LiveCoaching
Metrics for the user as the host being coached. View/comment counts require an Admin or Team Manager coach. Without qualifying coaching activity the object contains zero counts and lastCoachedAt: null. Open LiveCoachingMetrics →
| Field | Type | Description |
|---|---|---|
sessionsHosted | number | Rooms created in the window for hosts with qualifying coach views or comments in the window. Not all rooms for all hosts. |
viewEventsReceived | number | View events received on hosted sessions. |
uniqueViewers | number | Unique viewers across hosted sessions. |
viewDurationMinutes | number | Sum of viewer participant durations for qualifying hosts, in minutes. Participants are selected by join time; duration can extend beyond the export end time. |
commentsReceived | number | Comments received during live coaching. |
uniqueCommenters | number | Unique commenters during live coaching. |
moneySaved | number | Sum of positive appointment prices attributed by the live-coaching rule: a qualifying sold outcome and a coach view within 7 calendar days before or after the appointment date. Not a proven causal savings amount; no currency code is returned. |
lastCoachedAt | string | null | Timestamp of the last live coaching session (ISO 8601), or null. |
Code Examples
Fetch all conversations (paginated)
import requests
def fetch_all_conversations(from_date, to_date, api_key):
url = "https://customer.rillavoice.com/export/conversations"
headers = {
"Authorization": api_key,
"Content-Type": "application/json"
}
conversations = []
page = 1
total_pages = 1
while page <= total_pages:
payload = {"fromDate": from_date, "toDate": to_date, "page": page, "limit": 50}
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
data = response.json()
conversations.extend(data["conversations"])
total_pages = data["totalPages"]
page += 1
return conversationsasync function fetchAllConversations(fromDate, toDate, apiKey) {
const conversations = []
let page = 1
let totalPages = 1
do {
const res = await fetch('https://customer.rillavoice.com/export/conversations', {
method: 'POST',
headers: {
Authorization: apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({ fromDate, toDate, page, limit: 50 }),
})
if (!res.ok) throw new Error(`Export failed: ${res.status}`)
const data = await res.json()
conversations.push(...data.conversations)
totalPages = data.totalPages
page++
} while (page <= totalPages)
return conversations
}curl -X POST https://customer.rillavoice.com/export/conversations \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fromDate": "2026-09-01T00:00:00Z", "toDate": "2026-09-02T00:00:00Z", "page": 1, "limit": 50}'Fetch a single conversation by ID
import requests
def fetch_conversation(conversation_id, api_key):
url = f"https://customer.rillavoice.com/export/conversations/{conversation_id}"
headers = {"Authorization": api_key}
response = requests.get(url, headers=headers)
if response.status_code == 404:
return None
response.raise_for_status()
return response.json()async function fetchConversation(conversationId, apiKey) {
const res = await fetch(
`https://customer.rillavoice.com/export/conversations/${conversationId}`,
{ headers: { Authorization: apiKey } },
)
if (res.status === 404) return null
if (!res.ok) throw new Error(`Request failed: ${res.status}`)
return res.json()
}curl https://customer.rillavoice.com/export/conversations/f1a3e9d8-2b46-4c1a-9c7e-7e4a18d2c93f \
-H "Authorization: YOUR_API_KEY"Fetch teams
import requests
url = "https://customer.rillavoice.com/export/teams"
headers = {
"Authorization": "YOUR_API_KEY",
"Content-Type": "application/json"
}
payload = {
"fromDate": "2026-09-01T00:00:00Z",
"toDate": "2026-09-02T00:00:00Z",
"includeSubteams": True
}
response = requests.post(url, json=payload, headers=headers)
print(response.status_code, response.json())const response = await fetch('https://customer.rillavoice.com/export/teams', {
method: 'POST',
headers: {
Authorization: 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fromDate: '2026-09-01T00:00:00Z',
toDate: '2026-09-02T00:00:00Z',
includeSubteams: true,
}),
})curl -X POST https://customer.rillavoice.com/export/teams \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fromDate": "2026-09-01T00:00:00Z", "toDate": "2026-09-02T00:00:00Z", "includeSubteams": true}'Fetch users
import requests
url = "https://customer.rillavoice.com/export/users"
headers = {
"Authorization": "YOUR_API_KEY",
"Content-Type": "application/json"
}
payload = {
"fromDate": "2026-09-01T00:00:00Z",
"toDate": "2026-09-02T00:00:00Z"
}
response = requests.post(url, json=payload, headers=headers)
print(response.status_code, response.json())const response = await fetch('https://customer.rillavoice.com/export/users', {
method: 'POST',
headers: {
Authorization: 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fromDate: '2026-09-01T00:00:00Z',
toDate: '2026-09-02T00:00:00Z',
}),
})curl -X POST https://customer.rillavoice.com/export/users \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fromDate": "2026-09-01T00:00:00Z", "toDate": "2026-09-02T00:00:00Z"}'Error Codes
Data Export errors are JSON objects with a message field. Validation errors also include an errors array with field paths. This differs from Custom CRM’s normally plain-text responses.
{"message":"Invalid JSON in request body"}Use straight double quotes in JSON, not smart quotes, and remove trailing commas. Check syntax locally with python3 -m json.tool request.json, or use the browser-only JSON checker on each endpoint page.
| Code | Status | Description |
|---|---|---|
200 | OK | The request was processed successfully. |
400 | Bad Request | Malformed request or missing required fields. Common causes: fromDate or toDate missing; invalid date format; page or limit not a positive integer; limit exceeds 50; users array contains an invalid email; body is not valid JSON. |
401 | Unauthorized | Authorization header missing or API key not recognized. |
404 | Not Found | The requested conversation does not exist, or is outside the scope of your API key. Returned by GET /export/conversations/{conversationId}. |
500 | Internal Server Error | Unexpected server error. |
400 and 404 errors are permanent — retrying the same request will not help. 401 errors indicate auth problems — verify the Authorization header is set to your raw API key (no prefix). 500 errors may be transient — retry with exponential backoff.
Rate Limits
The Data Export API does not enforce application-level rate limits. There is no per-key or per-IP throttling built into the API itself.
Effective limits depend on the deployed infrastructure and key configuration. AWS regional defaults are not a per-customer quota or throughput guarantee. Confirm your intended volume with Rilla.
High-volume Usage
If your integration requires high-throughput or bulk export operations, reach out to support@rilla.com before going live.
Best Practices
- Retry with exponential backoff on
429or5xxresponses. - Page through results incrementally rather than requesting all data at once.
- Narrow your date ranges to reduce response size and processing time.
Pagination
The /export/conversations endpoint returns results one page at a time. Use page and limit to control which slice of results you receive, and totalPages to know when to stop.
Request Parameters
| Parameter | Default | Max | Description |
|---|---|---|---|
page | 1 | — | 1-based page index. |
limit | 50 | 50 | Conversations per page. |
Response Fields
| Field | Description |
|---|---|
currentPage | The page number of the current response. |
totalPages | Total number of pages for the date range, across the conversations, awaitingUpload, and errored arrays. |
totalConversations | Total conversations across all pages. |
totalAwaitingUpload | Total recordings awaiting upload across all pages. Omitted when dateType is processedDate. |
totalErrored | Total errored recordings across all pages. |
conversations | Array of conversation objects on this page. |
awaitingUpload | Array of recordings awaiting upload on this page. Omitted when dateType is processedDate. |
errored | Array of errored recordings on this page. |
The three arrays are paginated independently by the same page and limit. totalPages covers whichever array has the most pages, so pages near the end may return empty arrays for sets that are already exhausted.
Fetching all pages
Loop from page 1 to totalPages, incrementing page each iteration.
async function fetchAllConversations(fromDate, toDate, apiKey) {
const conversations = []
let page = 1
let totalPages = 1
do {
const res = await fetch('https://customer.rillavoice.com/export/conversations', {
method: 'POST',
headers: {
Authorization: apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({ fromDate, toDate, page, limit: 50 }),
})
if (!res.ok) throw new Error(`Export failed: ${res.status}`)
const data = await res.json()
conversations.push(...data.conversations)
totalPages = data.totalPages
page++
} while (page <= totalPages)
return conversations
}Only /export/conversations is paginated. /export/teams and /export/users return all results in a single response.