curl --request GET \
--url https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls \
--header 'Authorization: Bearer <token>' \
--header 'X-Client-Id: <x-client-id>'import requests
url = "https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls"
headers = {
"X-Client-Id": "<x-client-id>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'X-Client-Id': '<x-client-id>', Authorization: 'Bearer <token>'}
};
fetch('https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"X-Client-Id: <x-client-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Client-Id", "<x-client-id>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls")
.header("X-Client-Id", "<x-client-id>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Client-Id"] = '<x-client-id>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"page": 0,
"per_page": 50,
"total_pages": 3,
"total_count": 122,
"content": [
{
"id": "5f0f4ca5-9e4a-44ad-b26e-cf13cb1bf922",
"created_at": "2026-04-10T18:00:00Z",
"practice_name": "Alpha Veterinary Clinic",
"direction": "inbound",
"state": "completed",
"caller_type": "new",
"client_id": "8a4b1c2d-3e5f-4a6b-8c7d-9e0f1a2b3c4d",
"remote_client_id": "1234-56",
"client_name": "Dana Reed",
"patient_id": "b7c8d9e0-f1a2-4b3c-8d5e-6f7a8b9c0d1e",
"remote_patient_id": "7890-56",
"patient_name": "Rufus",
"phone_number": "+15125550123",
"call_classification": "scheduling",
"summary": "Booked a wellness exam",
"customer_sentiment": "happy",
"outcome": "appointment_booked",
"booked": true,
"booking_intent": true,
"duration_seconds": 125,
"started_at": "2026-04-10T18:00:02Z",
"ended_at": "2026-04-10T18:02:07Z",
"transferred_at": null,
"transfer_outcome": null,
"transfer_reason": null,
"transfer_type": null,
"transfer_dial_duration_seconds": null,
"transfer_attempts": []
}
]
},
"message": null
}{
"success": true,
"message": "<string>",
"data": {}
}{
"success": true,
"message": "<string>",
"data": {}
}{
"success": true,
"message": "<string>",
"data": {}
}List Phone Calls
List AI voice agent call records for a practice. Returns JSON by default; pass format=csv for a CSV batch download with the same rows.
curl --request GET \
--url https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls \
--header 'Authorization: Bearer <token>' \
--header 'X-Client-Id: <x-client-id>'import requests
url = "https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls"
headers = {
"X-Client-Id": "<x-client-id>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'X-Client-Id': '<x-client-id>', Authorization: 'Bearer <token>'}
};
fetch('https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"X-Client-Id: <x-client-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Client-Id", "<x-client-id>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls")
.header("X-Client-Id", "<x-client-id>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Client-Id"] = '<x-client-id>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"page": 0,
"per_page": 50,
"total_pages": 3,
"total_count": 122,
"content": [
{
"id": "5f0f4ca5-9e4a-44ad-b26e-cf13cb1bf922",
"created_at": "2026-04-10T18:00:00Z",
"practice_name": "Alpha Veterinary Clinic",
"direction": "inbound",
"state": "completed",
"caller_type": "new",
"client_id": "8a4b1c2d-3e5f-4a6b-8c7d-9e0f1a2b3c4d",
"remote_client_id": "1234-56",
"client_name": "Dana Reed",
"patient_id": "b7c8d9e0-f1a2-4b3c-8d5e-6f7a8b9c0d1e",
"remote_patient_id": "7890-56",
"patient_name": "Rufus",
"phone_number": "+15125550123",
"call_classification": "scheduling",
"summary": "Booked a wellness exam",
"customer_sentiment": "happy",
"outcome": "appointment_booked",
"booked": true,
"booking_intent": true,
"duration_seconds": 125,
"started_at": "2026-04-10T18:00:02Z",
"ended_at": "2026-04-10T18:02:07Z",
"transferred_at": null,
"transfer_outcome": null,
"transfer_reason": null,
"transfer_type": null,
"transfer_dial_duration_seconds": null,
"transfer_attempts": []
}
]
},
"message": null
}{
"success": true,
"message": "<string>",
"data": {}
}{
"success": true,
"message": "<string>",
"data": {}
}{
"success": true,
"message": "<string>",
"data": {}
}format=csv.
Filtering
Dates areYYYY-MM-DD and inclusive on both ends — start_date=2026-04-01&end_date=2026-04-30
returns the whole month. Any other date format returns a 400.
Unknown values for direction, state, or call_classification return a 400 rather than
silently matching nothing. Pass outcome more than once (outcome[]=appointment_booked&outcome[]=task_created)
to match any of several outcomes.
Field definitions
caller_type—existingif the matched client (or one of their patients) had a visit before this call,newif the caller matched a client with no prior visit,unknownif the call was never matched to a client.booked— the call ended in a scheduling outcome (appointment_booked,appointment_rescheduled,appointment_confirmed).booking_intent— the caller wanted to book or change an appointment. True when any of: the call was classified asscheduling, the outcome was a scheduling outcome, or the AI invoked a scheduling tool (book, reschedule, cancel, or confirm an appointment) during the call. The last two conditions catch calls the classifier filed under a different intent. It is derived at read time, so it is populated for historical calls too.summary— the AI’s one-line summary of the call.remote_client_id/remote_patient_id— the client’s and patient’s ids in your practice management system, qualified with the practice’s server id when it has one. These are the same values returned by the appointments endpoints, so records line up across both.started_at/ended_at— when the call connected and when it ended.created_atis when Oliver first recorded the call, which can be a moment earlier thanstarted_at.
Transfers
When the AI hands a caller to staff, these fields describe the most recent transfer attempt. They are allnull if the call was never transferred.
transferred_at— when the AI started the transfer.transfer_outcome—answered,no_answer,busyorfailed.transfer_reason— the AI’s reason for the transfer, in its own words.transfer_type— whether the caller asked for a person (caller_requested, orimmediate_caller_requestedwhen they asked straight away) or Oliver decided to transfer (agent_initiated_urgent,agent_initiated_topic,specialty,agent_failed_to_resolve,system_error,group_router).transfer_dial_duration_seconds— how long the caller was connected to staff once they answered.
transfer_attempts keeps every attempt, oldest first, with the line that was dialed:
"transfer_attempts": [
{ "attempted_at": "2026-04-10T18:01:00Z", "destination_label": "Front Desk",
"destination_number": "+15125550100", "destination_extension": null },
{ "attempted_at": "2026-04-10T18:01:40Z", "destination_label": "Urgent line",
"destination_number": "+15125550199", "destination_extension": "12" }
]
CSV download
Addformat=csv (or send Accept: text/csv) to get the same rows as a CSV attachment:
curl -L 'https://partner-api.getoliver.com/services/core/open_api/v1/phone_calls?start_date=2026-04-01&end_date=2026-04-30&format=csv&per_page=5000' \
-H 'Authorization: Bearer <token>' \
-H 'X-Client-Id: <location id>' \
-o phone_calls_april.csv
Date, Practice, Direction, Status, Caller Type, Client, Patient, Phone Number,
Intent, Booking Intent, Summary, Sentiment, Outcome, Booked, Duration,
Started At, Ended At, Transferred At, Transfer Outcome, Transfer Reason, Transfer Type,
Transfer Dial Duration, Transfer Destinations
Date and the other timestamps are rendered in the practice timezone as YYYY-MM-DD HH:MM,
durations as m:ss, and the yes/no columns as Yes/No. Transfer Destinations lists every
attempt in order as Label (number xextension), separated by ; .
The Intent, Sentiment, Outcome, Transfer Outcome and Transfer Type columns contain humanized
labels of the JSON enum values — Scheduling for call_classification: scheduling,
Appointment booked for outcome: appointment_booked, and so on — so lowercase and replace
spaces with underscores to map a CSV value back to its JSON enum.
per_page defaults to 1000 and allows up to 5000. The
paging metadata comes back in response headers (X-Total-Count, X-Page, X-Per-Page,
X-Total-Pages) so you can page through a wide date range without guessing how many pages there
are. page is zero-based in both formats.Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Headers
The location id of the practice
Query Parameters
Only return calls created on or after this date, inclusive. Interpreted as a whole day in the practice timezone.
"2026-04-01"
Only return calls created on or before this date, inclusive. Interpreted as a whole day in the practice timezone.
"2026-04-30"
Filter by call direction.
inbound, outbound Filter by the call's lifecycle state.
pending, queued, ringing, in_progress, completed, failed, no_answer, busy, voicemail, canceled Filter by call outcome. Repeat the parameter (outcome[]=a&outcome[]=b) to match any of several outcomes.
appointment_booked, appointment_rescheduled, appointment_confirmed, appointment_canceled, callback_requested, declined, do_not_call, no_answer, voicemail, forwarded, transferred, quick_abandoned, abandoned, abandoned_after_transfer, dropped, transfer_completed, transfer_no_human, transfer_caller_abandoned, task_created, resolved Filter by the AI's classification of why the caller called.
scheduling, prescription_refill, medical_question, billing, records_request, emergency, callback, general_inquiry, price_shopping, other Filter to calls where the caller did (true) or did not (false) want to book or change an appointment. See the endpoint page for how this is derived.
Only return calls matched to this Oliver client.
Page number (0-based)
Number of results per page. JSON: default 50, max 100. CSV: default 1000, max 5000.
Set to 'csv' to download the same rows as a CSV attachment instead of JSON. Sending 'Accept: text/csv' has the same effect.
csv Response
Phone calls response. Returns JSON by default, or a CSV attachment when format=csv. The CSV response also carries X-Total-Count, X-Page (0-based), X-Per-Page and X-Total-Pages headers so a wide date range can be paged through without guessing.

