curl --request POST \
--url https://api.teamfollowup.ai/api/native-calendar/calendars/preview \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"calendarId": "cal_6b1f2a7c90",
"calendar": {
"timezone": "America/Chicago",
"weeklyHours": {
"mon": [
{
"start": "08:00",
"end": "18:00"
}
]
},
"slotDurationMinutes": 45,
"minNoticeMinutes": 60
}
}'import requests
url = "https://api.teamfollowup.ai/api/native-calendar/calendars/preview"
payload = { "calendar": {
"timezone": "America/Chicago",
"weeklyHours": { "mon": [
{
"start": "08:00",
"end": "18:00"
}
] },
"slotDurationMinutes": 45,
"minNoticeMinutes": 60
} }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
calendar: {
timezone: 'America/Chicago',
weeklyHours: {mon: [{start: '08:00', end: '18:00'}]},
slotDurationMinutes: 45,
minNoticeMinutes: 60
}
})
};
fetch('https://api.teamfollowup.ai/api/native-calendar/calendars/preview', 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.teamfollowup.ai/api/native-calendar/calendars/preview",
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([
'calendar' => [
'timezone' => 'America/Chicago',
'weeklyHours' => [
'mon' => [
[
'start' => '08:00',
'end' => '18:00'
]
]
],
'slotDurationMinutes' => 45,
'minNoticeMinutes' => 60
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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.teamfollowup.ai/api/native-calendar/calendars/preview"
payload := strings.NewReader("{\n \"calendar\": {\n \"timezone\": \"America/Chicago\",\n \"weeklyHours\": {\n \"mon\": [\n {\n \"start\": \"08:00\",\n \"end\": \"18:00\"\n }\n ]\n },\n \"slotDurationMinutes\": 45,\n \"minNoticeMinutes\": 60\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.teamfollowup.ai/api/native-calendar/calendars/preview")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"calendar\": {\n \"timezone\": \"America/Chicago\",\n \"weeklyHours\": {\n \"mon\": [\n {\n \"start\": \"08:00\",\n \"end\": \"18:00\"\n }\n ]\n },\n \"slotDurationMinutes\": 45,\n \"minNoticeMinutes\": 60\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.teamfollowup.ai/api/native-calendar/calendars/preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"calendar\": {\n \"timezone\": \"America/Chicago\",\n \"weeklyHours\": {\n \"mon\": [\n {\n \"start\": \"08:00\",\n \"end\": \"18:00\"\n }\n ]\n },\n \"slotDurationMinutes\": 45,\n \"minNoticeMinutes\": 60\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"timezone": "America/Chicago",
"slots": {
"2026-09-24": {
"slots": [
"2026-09-24T14:00:00.000Z",
"2026-09-24T14:30:00.000Z",
"2026-09-24T15:00:00.000Z"
]
},
"2026-09-25": {
"slots": [
"2026-09-25T14:00:00.000Z"
]
}
}
}
}{
"success": false,
"error": "timezone is required"
}{
"success": false,
"error": "Unauthorized"
}{
"success": false,
"error": "calendar does not belong to this project"
}{
"success": false,
"error": "calendar not found"
}{
"success": false,
"error": "TooManyRequests"
}{
"success": false,
"error": "Calendar request failed."
}{
"success": false,
"error": "Calendar service is unreachable."
}Preview slots for a calendar definition
The slots a calendar definition WOULD offer, without saving it.
Send the whole definition under calendar. It is validated exactly as a create is, so this also answers “is this set of hours even legal” before anyone commits to it.
Pass calendarId as well to preview a change to a calendar that exists: what is already booked on it is counted, so the preview shows what would really be left rather than an empty diary.
locationId always comes from the tenant, never from the definition you submit.
Required API key scope: projects:read.
curl --request POST \
--url https://api.teamfollowup.ai/api/native-calendar/calendars/preview \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"calendarId": "cal_6b1f2a7c90",
"calendar": {
"timezone": "America/Chicago",
"weeklyHours": {
"mon": [
{
"start": "08:00",
"end": "18:00"
}
]
},
"slotDurationMinutes": 45,
"minNoticeMinutes": 60
}
}'import requests
url = "https://api.teamfollowup.ai/api/native-calendar/calendars/preview"
payload = { "calendar": {
"timezone": "America/Chicago",
"weeklyHours": { "mon": [
{
"start": "08:00",
"end": "18:00"
}
] },
"slotDurationMinutes": 45,
"minNoticeMinutes": 60
} }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
calendar: {
timezone: 'America/Chicago',
weeklyHours: {mon: [{start: '08:00', end: '18:00'}]},
slotDurationMinutes: 45,
minNoticeMinutes: 60
}
})
};
fetch('https://api.teamfollowup.ai/api/native-calendar/calendars/preview', 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.teamfollowup.ai/api/native-calendar/calendars/preview",
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([
'calendar' => [
'timezone' => 'America/Chicago',
'weeklyHours' => [
'mon' => [
[
'start' => '08:00',
'end' => '18:00'
]
]
],
'slotDurationMinutes' => 45,
'minNoticeMinutes' => 60
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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.teamfollowup.ai/api/native-calendar/calendars/preview"
payload := strings.NewReader("{\n \"calendar\": {\n \"timezone\": \"America/Chicago\",\n \"weeklyHours\": {\n \"mon\": [\n {\n \"start\": \"08:00\",\n \"end\": \"18:00\"\n }\n ]\n },\n \"slotDurationMinutes\": 45,\n \"minNoticeMinutes\": 60\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.teamfollowup.ai/api/native-calendar/calendars/preview")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"calendar\": {\n \"timezone\": \"America/Chicago\",\n \"weeklyHours\": {\n \"mon\": [\n {\n \"start\": \"08:00\",\n \"end\": \"18:00\"\n }\n ]\n },\n \"slotDurationMinutes\": 45,\n \"minNoticeMinutes\": 60\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.teamfollowup.ai/api/native-calendar/calendars/preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"calendar\": {\n \"timezone\": \"America/Chicago\",\n \"weeklyHours\": {\n \"mon\": [\n {\n \"start\": \"08:00\",\n \"end\": \"18:00\"\n }\n ]\n },\n \"slotDurationMinutes\": 45,\n \"minNoticeMinutes\": 60\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"timezone": "America/Chicago",
"slots": {
"2026-09-24": {
"slots": [
"2026-09-24T14:00:00.000Z",
"2026-09-24T14:30:00.000Z",
"2026-09-24T15:00:00.000Z"
]
},
"2026-09-25": {
"slots": [
"2026-09-25T14:00:00.000Z"
]
}
}
}
}{
"success": false,
"error": "timezone is required"
}{
"success": false,
"error": "Unauthorized"
}{
"success": false,
"error": "calendar does not belong to this project"
}{
"success": false,
"error": "calendar not found"
}{
"success": false,
"error": "TooManyRequests"
}{
"success": false,
"error": "Calendar request failed."
}{
"success": false,
"error": "Calendar service is unreachable."
}Authorizations
Send your API key as Authorization: Bearer YOUR_API_KEY.
Query Parameters
The sub-account this calendar belongs to. It is the only thing that identifies the tenant here, and access is re-checked against it on every call.
Body
The definition to try.
