curl --request POST \
--url https://api.cal.com/v2/discover/business-calls \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '
{
"businessUrl": "https://barbers.example/book",
"service": "upright piano full tuning and pitch raise",
"timing": "Thursday after 5 or Friday morning",
"staff": "Sam",
"notes": "allergic to lavender",
"daysFromToday": 1,
"callerName": "Jamie Rivera"
}
'import requests
url = "https://api.cal.com/v2/discover/business-calls"
payload = {
"businessUrl": "https://barbers.example/book",
"service": "upright piano full tuning and pitch raise",
"timing": "Thursday after 5 or Friday morning",
"staff": "Sam",
"notes": "allergic to lavender",
"daysFromToday": 1,
"callerName": "Jamie Rivera"
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': 'application/json'},
body: JSON.stringify({
businessUrl: 'https://barbers.example/book',
service: 'upright piano full tuning and pitch raise',
timing: 'Thursday after 5 or Friday morning',
staff: 'Sam',
notes: 'allergic to lavender',
daysFromToday: 1,
callerName: 'Jamie Rivera'
})
};
fetch('https://api.cal.com/v2/discover/business-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://api.cal.com/v2/discover/business-calls",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'businessUrl' => 'https://barbers.example/book',
'service' => 'upright piano full tuning and pitch raise',
'timing' => 'Thursday after 5 or Friday morning',
'staff' => 'Sam',
'notes' => 'allergic to lavender',
'daysFromToday' => 1,
'callerName' => 'Jamie Rivera'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.cal.com/v2/discover/business-calls"
payload := strings.NewReader("{\n \"businessUrl\": \"https://barbers.example/book\",\n \"service\": \"upright piano full tuning and pitch raise\",\n \"timing\": \"Thursday after 5 or Friday morning\",\n \"staff\": \"Sam\",\n \"notes\": \"allergic to lavender\",\n \"daysFromToday\": 1,\n \"callerName\": \"Jamie Rivera\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.cal.com/v2/discover/business-calls")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"businessUrl\": \"https://barbers.example/book\",\n \"service\": \"upright piano full tuning and pitch raise\",\n \"timing\": \"Thursday after 5 or Friday morning\",\n \"staff\": \"Sam\",\n \"notes\": \"allergic to lavender\",\n \"daysFromToday\": 1,\n \"callerName\": \"Jamie Rivera\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cal.com/v2/discover/business-calls")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"businessUrl\": \"https://barbers.example/book\",\n \"service\": \"upright piano full tuning and pitch raise\",\n \"timing\": \"Thursday after 5 or Friday morning\",\n \"staff\": \"Sam\",\n \"notes\": \"allergic to lavender\",\n \"daysFromToday\": 1,\n \"callerName\": \"Jamie Rivera\"\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"placed": true,
"callId": "call_8f2c1d",
"callUrl": "https://app.cal.com/discover/call/call_8f2c1d",
"reason": "The assistant cannot call businesses for this account.",
"signInRequired": true,
"paymentRequired": true,
"planUrl": "https://app.cal.com/settings/discover/preferences",
"businessClosed": {
"nextCallAt": "2026-11-13T09:00:00-08:00",
"timeZone": "America/Los_Angeles",
"hoursAssumed": false
}
}
}Call a web-found business to book
Has Cal’s voice assistant call a business on the user’s behalf to book the service, the way Cal.ai does once the user approves. Only a business that GET /v2/discover/listings/search returned in webBusinesses to this same user in the last 24 hours can be called, with its URL exactly as returned; a Discover listing is never called, because it is booked from its bookingUrl. Every check runs before the call: the account may place calls, the business’s page offers a US or Canadian appointment phone, the name and service are plain words, the business was not called too recently, and the user’s plan covers it. The call is placed by Cal’s AI voice assistant, so a user in an organization that opted out of AI cannot place calls. A call that is not placed answers placed: false with the reason. A refusal spends neither daily limit, except a refusal for calling hours found after Cal set up the call, which keeps that business’s count for the day. If dialling fails after the call was started, the user’s call is given back when the provider refused the dial, but that business’s count for the day is not; a dial whose outcome is unknown counts against both, since the business may have been rung. A placed call answers with callUrl, the call’s page, where the user follows the call and its outcome. Calls are limited to 5 a day per user, shared across all of the user’s API keys and access tokens, and to 10 a day per business across all users. If accessed using an OAuth access token, the PROFILE_WRITE scope is required.
curl --request POST \
--url https://api.cal.com/v2/discover/business-calls \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '
{
"businessUrl": "https://barbers.example/book",
"service": "upright piano full tuning and pitch raise",
"timing": "Thursday after 5 or Friday morning",
"staff": "Sam",
"notes": "allergic to lavender",
"daysFromToday": 1,
"callerName": "Jamie Rivera"
}
'import requests
url = "https://api.cal.com/v2/discover/business-calls"
payload = {
"businessUrl": "https://barbers.example/book",
"service": "upright piano full tuning and pitch raise",
"timing": "Thursday after 5 or Friday morning",
"staff": "Sam",
"notes": "allergic to lavender",
"daysFromToday": 1,
"callerName": "Jamie Rivera"
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': 'application/json'},
body: JSON.stringify({
businessUrl: 'https://barbers.example/book',
service: 'upright piano full tuning and pitch raise',
timing: 'Thursday after 5 or Friday morning',
staff: 'Sam',
notes: 'allergic to lavender',
daysFromToday: 1,
callerName: 'Jamie Rivera'
})
};
fetch('https://api.cal.com/v2/discover/business-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://api.cal.com/v2/discover/business-calls",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'businessUrl' => 'https://barbers.example/book',
'service' => 'upright piano full tuning and pitch raise',
'timing' => 'Thursday after 5 or Friday morning',
'staff' => 'Sam',
'notes' => 'allergic to lavender',
'daysFromToday' => 1,
'callerName' => 'Jamie Rivera'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.cal.com/v2/discover/business-calls"
payload := strings.NewReader("{\n \"businessUrl\": \"https://barbers.example/book\",\n \"service\": \"upright piano full tuning and pitch raise\",\n \"timing\": \"Thursday after 5 or Friday morning\",\n \"staff\": \"Sam\",\n \"notes\": \"allergic to lavender\",\n \"daysFromToday\": 1,\n \"callerName\": \"Jamie Rivera\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.cal.com/v2/discover/business-calls")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"businessUrl\": \"https://barbers.example/book\",\n \"service\": \"upright piano full tuning and pitch raise\",\n \"timing\": \"Thursday after 5 or Friday morning\",\n \"staff\": \"Sam\",\n \"notes\": \"allergic to lavender\",\n \"daysFromToday\": 1,\n \"callerName\": \"Jamie Rivera\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cal.com/v2/discover/business-calls")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"businessUrl\": \"https://barbers.example/book\",\n \"service\": \"upright piano full tuning and pitch raise\",\n \"timing\": \"Thursday after 5 or Friday morning\",\n \"staff\": \"Sam\",\n \"notes\": \"allergic to lavender\",\n \"daysFromToday\": 1,\n \"callerName\": \"Jamie Rivera\"\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"placed": true,
"callId": "call_8f2c1d",
"callUrl": "https://app.cal.com/discover/call/call_8f2c1d",
"reason": "The assistant cannot call businesses for this account.",
"signInRequired": true,
"paymentRequired": true,
"planUrl": "https://app.cal.com/settings/discover/preferences",
"businessClosed": {
"nextCallAt": "2026-11-13T09:00:00-08:00",
"timeZone": "America/Los_Angeles",
"hoursAssumed": false
}
}
}Headers
value must be Bearer <token> where <token> is api key prefixed with cal_, managed user access token, or OAuth access token
Body
The business's URL, exactly as a search_listings web business gave it in webBusinesses[].url. Only a business found by your own search in the last 24 hours can be called.
2048"https://barbers.example/book"
What to book, in plain words, such as upright piano full tuning and pitch raise. Put when the user can come in timing, the staff member they asked for in staff and their booking details in notes. Never put contact details (phone, email, address) in it.
1 - 120"upright piano full tuning and pitch raise"
When the user can come, in their words, such as Thursday after 5 or Friday morning. Leave it out when they named no window. Never put contact details in it.
1 - 120"Thursday after 5 or Friday morning"
The staff member the user asked for, in their words, such as Sam. Leave it out when they named none; never take one from the business's page.
1 - 80"Sam"
Booking details the user gave for this booking, such as allergic to lavender. Only what they said, never contact details. Leave it out when they gave none.
1 - 200"allergic to lavender"
The day the user asked for, in days from today in their saved time zone: 0 today, 1 tomorrow. Cal asks the business for that date. Leave it out when the user named no day.
0 <= x <= 601
The name the user gave for themselves, which Cal gives on the call. Leave it out to use the account name; never a name from a search result.
1 - 80"Jamie Rivera"
Was this page helpful?