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.
To ask questions about the same data in Claude, ChatGPT or Cursor instead, connect Rilla MCP.
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.
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"
},
"rewindsCompleted": 3,
"rewindAverageScore": 78.5,
"trainingLessonsCompleted": 5,
"trainingAverageScore": 82.25
}
]
}GET /export/users
Lists the users your API key can access, sorted by name, without metrics. Use it to find a userId.
Request Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | API key. Format: YOUR_API_KEY. |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | No | Part of a full name or email, case-insensitive, up to 200 characters. |
teamId | string (UUID) | No | Only this team's direct members. |
role | string | No | Part of a role name, case-insensitive, up to 100 characters, such as Sales Rep, Team Manager or Admin, plus any role your organization added. |
removed | boolean | No | true for removed users only, false for active users only. Omit for both. |
page | integer | No | Page to return, from 1. Requires limit. |
limit | integer | No | Users per page, up to 10,000. Without it, every user in scope comes back on one page. |
Response
Returns a UserListResponse: users (UserListEntry[]), currentPage, totalPages and totalUsers.
Example Request
GET /export/users?query=alex HTTP/1.1
Host: customer.rillavoice.com
Authorization: YOUR_API_KEYExample Response
Returns UserListResponse. This example includes every documented field.
{
"users": [
{
"userId": "33333333-3333-4333-8333-333333333333",
"email": "alex@example.com",
"isRemoved": false,
"role": "Sales Rep",
"name": "Alex Morgan"
}
],
"currentPage": 1,
"totalPages": 1,
"totalUsers": 1
}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. |
Returns 403 Forbidden when your key is limited to teams that no longer exist.
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,
"rewindsCompleted": 3,
"rewindAverageScore": 78.5,
"trainingLessonsCompleted": 5,
"trainingAverageScore": 82.25
}
]
}GET /export/teams
Lists the teams your API key can access, sorted by name, without metrics. Use it to find a teamId.
Request Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | API key. Format: YOUR_API_KEY. |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | No | Part of a team name, case-insensitive, up to 200 characters. |
parentTeamId | string (UUID) | No | Only this team's direct subteams. |
page | integer | No | Page to return, from 1. Requires limit. |
limit | integer | No | Teams per page, up to 10,000. Without it, every team in scope comes back on one page. |
Response
Returns a TeamListResponse: teams (TeamListEntry[]), currentPage, totalPages and totalTeams. Returns 403 Forbidden when your key is limited to teams that no longer exist.
Example Request
GET /export/teams?query=West HTTP/1.1
Host: customer.rillavoice.com
Authorization: YOUR_API_KEYExample Response
Returns TeamListResponse. This example includes every documented field.
{
"teams": [
{
"teamId": "44444444-4444-4444-8444-444444444444",
"name": "West Region",
"parentTeamId": null,
"parentTeamName": null,
"numberOfUsers": 12
}
],
"currentPage": 1,
"totalPages": 1,
"totalTeams": 1
}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": 1,
"totalViewTimeMs": 270000,
"views": [
{
"viewId": "66666666-6666-4666-8666-666666666666",
"viewedAt": "2026-09-02T14:30:00.000Z",
"durationMs": 270000
}
]
}
],
"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
}
}
],
"comments": [
{
"commentId": "77777777-7777-4777-8777-777777777777",
"parentId": null,
"userId": "user-014",
"userName": "Manager Mike",
"content": "@Rep Rachel great discovery questions here",
"createdAt": "2026-09-02T14:30:00.000Z",
"attachment": {
"type": "AUDIO",
"mimeType": "audio/mp4",
"transcript": "Slow down when you get to price.",
"downloadUrl": "https://example.com/temporary-comment.m4a?signature=EXAMPLE"
},
"startTime": 12.5,
"endTime": 20.25
}
]
}
],
"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"
}
]
}GET /export/conversations
Lists the conversations in a date range that your API key can access, newest first. Each row is light: ID, dates, title, duration, rep and outcome. Use GET /export/conversations/{conversationId} for a conversation in full.
Request Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | API key. Format: YOUR_API_KEY. |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
fromDate | string | Yes | Start of the period; a UTC timestamp ending in Z. Included. |
toDate | string | Yes | End of the period; a UTC timestamp ending in Z, on or after fromDate. Included. |
dateType | string | No | timeOfRecording (default) selects and sorts by when the conversation was recorded; processedDate by when it finished processing. |
userIds | string[] | No | Only these users' conversations, up to 100 user IDs, comma-separated or repeated. Send this or teamId, not both. |
teamId | string (UUID) | No | Only this team's users' conversations. Send this or userIds, not both. |
includeSubteams | boolean | No | With teamId, also include the users of the team's subteams. true without teamId is rejected. |
query | string | No | Part of the title or job number, case-insensitive, up to 200 characters. Titles usually hold the customer name. |
outcome | string | No | Only conversations with this outcome as the rows show it, case-insensitive, up to 100 characters: sold matches Sold but not Not Sold. |
minDuration | integer | No | Only conversations at least this many seconds long, up to 86400. |
order | string | No | desc (default) for newest first, asc for oldest first. |
page | integer | No | Page to return, from 1, up to 10,000. Defaults to 1. |
limit | integer | No | Conversations per page, up to 100. Defaults to 100. |
Response
Returns a ConversationListResponse: conversations (ConversationListEntry[]), currentPage, totalPages and totalConversations. This list is always paginated. Removed users' conversations are included. With teamId, returns 403 Forbidden when your key is limited to teams that no longer exist.
Example Request
GET /export/conversations?fromDate=2026-09-01T00:00:00Z&toDate=2026-09-02T00:00:00Z&query=Lee HTTP/1.1
Host: customer.rillavoice.com
Authorization: YOUR_API_KEYExample Response
Returns ConversationListResponse. This example includes every documented field.
{
"conversations": [
{
"conversationId": "11111111-1111-4111-8111-111111111111",
"date": "2026-09-01T14:00:00Z",
"processedDate": "2026-09-01T15:12:00Z",
"title": "Home estimate - Jordan Lee",
"duration": 3240,
"user": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Alex Morgan"
},
"outcome": "Sold"
}
],
"currentPage": 1,
"totalPages": 1,
"totalConversations": 1
}GET /export/conversations/{conversationId}
Fetches a single conversation by its ID. Returns the same conversation object as POST /export/conversations above, 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 POST /export/conversations, 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": 1,
"totalViewTimeMs": 270000,
"views": [
{
"viewId": "66666666-6666-4666-8666-666666666666",
"viewedAt": "2026-09-02T14:30:00.000Z",
"durationMs": 270000
}
]
}
],
"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
}
}
],
"comments": [
{
"commentId": "77777777-7777-4777-8777-777777777777",
"parentId": null,
"userId": "user-014",
"userName": "Manager Mike",
"content": "@Rep Rachel great discovery questions here",
"createdAt": "2026-09-02T14:30:00.000Z",
"attachment": {
"type": "AUDIO",
"mimeType": "audio/mp4",
"transcript": "Slow down when you get to price.",
"downloadUrl": "https://example.com/temporary-comment.m4a?signature=EXAMPLE"
},
"startTime": 12.5,
"endTime": 20.25
}
]
}GET /export/conversations/{conversationId}/transcript
Returns a conversation's transcript as speaker turns, in the order they were spoken.
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. |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page to return, from 1. Requires limit. |
limit | integer | No | Turns per page, up to 10,000. Without it, the whole transcript comes back on one page. |
Response
Returns a ConversationTranscriptResponse: conversationId, turns (TranscriptTurn[]), currentPage, totalPages and totalTurns. A long call can run to thousands of turns, so page with limit. A conversation with no transcript returns an empty first page. Returns 404 Not Found for a conversation that does not exist or falls outside your key's scope.
Example Request
GET /export/conversations/11111111-1111-4111-8111-111111111111/transcript?page=1&limit=100 HTTP/1.1
Host: customer.rillavoice.com
Authorization: YOUR_API_KEYExample Response
Returns ConversationTranscriptResponse. This example includes every documented field.
{
"conversationId": "11111111-1111-4111-8111-111111111111",
"turns": [
{
"speaker": "rep",
"start": 12,
"text": "Thanks for having me out today."
}
],
"currentPage": 1,
"totalPages": 1,
"totalTurns": 1
}GET /export/conversations/{conversationId}/survey-results
Returns the results of chosen surveys on one conversation. Pick the surveyIds from the conversation's surveyResults.
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. |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
surveyIds | string[] | Yes | 1 to 10 survey IDs, comma-separated or repeated. |
Response
Returns a ConversationSurveyResultsResponse: conversationId, surveyResults (SurveyConversationResult[]) and notFoundSurveyIds, the requested surveys the conversation has no results for. Returns 404 Not Found for a conversation that does not exist or falls outside your key's scope.
Example Request
GET /export/conversations/11111111-1111-4111-8111-111111111111/survey-results?surveyIds=55555555-5555-4555-8555-555555555555 HTTP/1.1
Host: customer.rillavoice.com
Authorization: YOUR_API_KEYExample Response
Returns ConversationSurveyResultsResponse. This example includes every documented field.
{
"conversationId": "11111111-1111-4111-8111-111111111111",
"surveyResults": [
{
"surveyId": "55555555-5555-4555-8555-555555555555",
"surveyName": "Discovery quality",
"data": {
"next_step_confirmed": true
}
}
],
"notFoundSurveyIds": []
}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. |
comments | Comment[] | All comments and replies left on the conversation, ordered by createdAt then commentId, not bounded by the request window. Private comments are left out, so it can be shorter than totalComments. See Comment. |
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. |
views | ViewEvent[] | All recorded views by this viewer, ordered by viewedAt then viewId. Export date filters select recording or processing dates, not these events. See ViewEvent. |
ViewEvent
One recorded view by the parent viewer, not a verified completed ride-along or unique viewing session. Deduplicate repeated exports by viewId. Open ViewEvent →
| Field | Type | Description |
|---|---|---|
viewId | string | Stable ID of the recorded view event; use to deduplicate repeated exports. |
viewedAt | string | When the view event was recorded, in ISO 8601 UTC. This is not necessarily when the viewing session started. |
durationMs | number | Recorded viewing duration in milliseconds. |
Comment
A comment or reply on the conversation. Private comments are not exported. Open Comment →
| Field | Type | Description |
|---|---|---|
commentId | string | Stable ID of the comment; use to deduplicate repeated exports. |
parentId | string | null | ID of the comment this one replies to; null for a top-level comment. |
userId | string | null | Rilla user ID of the author. Can belong to another organization when the conversation's rep moved organizations after the comment was posted. |
userName | string | null | Author's name; null when the author has no name. |
content | string | null | Comment text. Tagged users and teams appear as @ and their name, or @Unknown user / @Unknown team when the user or team no longer exists. null when the comment has no text, such as a voice, video or GIF comment. |
createdAt | string | When the comment was posted, in ISO 8601 UTC. |
attachment | CommentAttachment | null | The voice or video recording on this comment; null for a comment without one, including GIF comments, whose GIF is not exported. See CommentAttachment. |
startTime | number | null | Start of the commented moment, in seconds from the start of the recording; null when the comment is not on a moment. |
endTime | number | null | End of the commented moment, in seconds; null when startTime is null. |
CommentAttachment
The voice or video recording on a comment. Open CommentAttachment →
| Field | Type | Description |
|---|---|---|
type | string | AUDIO for a voice comment, VIDEO for a video. |
mimeType | string | null | MIME type of the recorded file. |
transcript | string | null | Transcript of the recording; null when it hasn't been transcribed or is silent. |
downloadUrl | string | null | Temporary presigned S3 URL for the 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. |
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. |
rewindsCompleted | number | Completed rewind sessions created in the inclusive date window; zero when none. |
rewindAverageScore | number | null | Mean feedback score of completed rewind sessions created in the inclusive date window, rounded to two decimals on a 0-100 scale; null without scores. |
trainingLessonsCompleted | number | Completed training lesson sessions created in the inclusive date window, including repeats of a lesson; zero when none. |
trainingAverageScore | number | null | Mean score of each team member's latest completed session per lesson created in the inclusive date window, with each user and lesson counted equally, rounded to two decimals on a 0-100 scale; null without scores. |
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. |
rewindsCompleted | number | Completed rewind sessions created in the inclusive date window; zero when none. |
rewindAverageScore | number | null | Mean feedback score of completed rewind sessions created in the inclusive date window, rounded to two decimals on a 0-100 scale; null without scores. |
trainingLessonsCompleted | number | Completed training lesson sessions created in the inclusive date window, including repeats of a lesson; zero when none. |
trainingAverageScore | number | null | Mean score of each user's latest completed session per lesson created in the inclusive date window, rounded to two decimals on a 0-100 scale; null without scores. |
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. |
ConversationListEntry
One conversation, without its transcript, coaching results or customer. Returned inside conversations by GET /export/conversations; use GET /export/conversations/{conversationId} for the full record. Open ConversationListEntry →
| Field | Type | Description |
|---|---|---|
conversationId | string | ID of the conversation. |
date | string | null | Recording start timestamp in ISO 8601 UTC. List selection uses inclusive date bounds. |
processedDate | string | null | Date/time the conversation finished processing (ISO 8601). |
title | string | null | Title of the conversation / appointment. |
duration | number | null | Processed conversation duration in seconds. |
user | ConversationListUser | The rep who recorded the conversation: { id, name }. |
outcome | string | null | Outcome of the appointment. |
TranscriptTurn
One speaker turn in a transcript. Open TranscriptTurn →
| Field | Type | Description |
|---|---|---|
speaker | string | Who spoke: the rep or the customer. |
start | number | When the turn starts, in whole seconds from the start of the recording. |
text | string | What was said. |
TeamListEntry
One team, without metrics. Returned inside teams by GET /export/teams. Open TeamListEntry →
| Field | Type | Description |
|---|---|---|
teamId | string | Rilla team ID. |
name | string | null | Name of the team. |
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. |
numberOfUsers | number | Active users in this team and all its subteams, each counted once. Removed and hidden users are not counted. |
UserListEntry
One user, without metrics. Returned inside users by GET /export/users. Open UserListEntry →
| Field | Type | Description |
|---|---|---|
userId | string | Rilla user ID. |
email | string | null | Email address. |
isRemoved | boolean | null | 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. |
name | string | null | First and last name joined by one space, with repeated spaces collapsed. Null when both are empty. |
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. On the GET /export/... routes, the message is Request body failed validation even for query parameters: for example page without limit, both userIds and teamId, includeSubteams=true without teamId, fromDate after toDate, limit above 100 on GET /export/conversations, or more than 10 surveyIds. |
401 | Unauthorized | Authorization header missing or API key not recognized. |
403 | Forbidden | Caller has no accessible export scope: the key is limited to teams that no longer exist. Returned by POST /export/teams, GET /export/teams, and GET /export/conversations with teamId. Create a key with current teams. |
404 | Not Found | The requested conversation does not exist, or is outside the scope of your API key. Returned by GET /export/conversations/{conversationId} and by the GET /export/conversations/{conversationId}/transcript and /survey-results routes. |
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
}Among the POST exports, only POST /export/conversations is paginated. POST /export/teams and POST /export/users return all results in a single response. The GET list routes page with page and limit; see each route above.