curl --request POST \
--url https://api.cal.id/teams/{teamId}/event-types \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "30 Minute Discovery Call",
"slug": "discovery-call-30"
}
'import requests
url = "https://api.cal.id/teams/{teamId}/event-types"
payload = {
"title": "30 Minute Discovery Call",
"slug": "discovery-call-30"
}
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({title: '30 Minute Discovery Call', slug: 'discovery-call-30'})
};
fetch('https://api.cal.id/teams/{teamId}/event-types', 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.id/teams/{teamId}/event-types",
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([
'title' => '30 Minute Discovery Call',
'slug' => 'discovery-call-30'
]),
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.cal.id/teams/{teamId}/event-types"
payload := strings.NewReader("{\n \"title\": \"30 Minute Discovery Call\",\n \"slug\": \"discovery-call-30\"\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.cal.id/teams/{teamId}/event-types")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"30 Minute Discovery Call\",\n \"slug\": \"discovery-call-30\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cal.id/teams/{teamId}/event-types")
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 \"title\": \"30 Minute Discovery Call\",\n \"slug\": \"discovery-call-30\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"id": 100933,
"title": "Sales Demo",
"slug": "sales-demo",
"description": "Guided 45 minute product walkthrough with the sales team.",
"interfaceLanguage": "en",
"position": 0,
"locations": [
{
"type": "integrations:daily"
}
],
"bookingFields": [
{
"name": "name",
"type": "name",
"required": true
},
{
"name": "email",
"type": "email",
"required": true
},
{
"name": "notes",
"type": "textarea",
"required": false
}
],
"length": 45,
"offsetStart": 0,
"hidden": false,
"userId": null,
"teamId": 812,
"profileId": null,
"timeZone": null,
"periodType": "UNLIMITED",
"periodStartDate": null,
"periodEndDate": null,
"periodDays": null,
"periodCountCalendarDays": null,
"lockTimeZoneToggleOnBookingPage": false,
"lockedTimeZone": null,
"requiresConfirmation": false,
"requiresConfirmationWillBlockSlot": false,
"requiresConfirmationForFreeEmail": false,
"requiresBookerEmailVerification": false,
"recurringEvent": null,
"disableGuests": false,
"hideCalendarNotes": false,
"hideCalendarEventDetails": false,
"minimumBookingNotice": 120,
"beforeEventBuffer": 0,
"afterEventBuffer": 10,
"seatsPerTimeSlot": null,
"onlyShowFirstAvailableSlot": false,
"disableCancelling": false,
"disableRescheduling": false,
"disableHostCancelling": false,
"disableHostRescheduling": false,
"seatsShowAttendees": null,
"seatsShowAvailabilityCount": null,
"schedulingType": "ROUND_ROBIN",
"scheduleId": 2231,
"price": 0,
"currency": "usd",
"slotInterval": null,
"metadata": {},
"successRedirectUrl": null,
"forwardParamsSuccessRedirect": true,
"bookingLimits": null,
"durationLimits": null,
"isInstantEvent": false,
"instantMeetingExpiryTimeOffsetInSeconds": 90,
"assignAllTeamMembers": false,
"assignRRMembersUsingSegment": false,
"rrSegmentQueryValue": null,
"useEventTypeDestinationCalendarEmail": false,
"isRRWeightsEnabled": false,
"maxLeadThreshold": null,
"includeNoShowInRRCalculation": false,
"allowReschedulingPastBookings": false,
"hideOrganizerEmail": false,
"maxActiveBookingsPerBooker": null,
"maxActiveBookingPerBookerOfferReschedule": false,
"customReplyToEmail": null,
"eventTypeColor": null,
"rescheduleWithSameRoundRobinHost": false,
"secondaryEmailId": null,
"useBookerTimezone": false,
"restrictionScheduleId": null,
"createdDate": "2026-02-11T10:04:18.000Z",
"updatedDate": "2026-04-28T15:31:07.000Z"
},
"message": "Team event type created successfully"
}{
"success": false,
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}
}{
"success": false,
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}
}{
"success": false,
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}
}{
"success": false,
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}
}{
"success": false,
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}
}{
"success": false,
"message": "Rate limit exceeded",
"error": {
"code": "TOO_MANY_REQUESTS",
"message": "Too many requests. Please retry after the rate limit window resets."
}
}Create a team event type
Creates an event type owned by a team. Alongside the usual title, slug and length, set schedulingType to decide how hosts are chosen — COLLECTIVE requires every host to be free, ROUND_ROBIN rotates between them, MANAGED pushes a copy to each member — and assign the hosts. Requires ADMIN or OWNER (403 otherwise); a slug already used in that team returns 409.
curl --request POST \
--url https://api.cal.id/teams/{teamId}/event-types \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "30 Minute Discovery Call",
"slug": "discovery-call-30"
}
'import requests
url = "https://api.cal.id/teams/{teamId}/event-types"
payload = {
"title": "30 Minute Discovery Call",
"slug": "discovery-call-30"
}
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({title: '30 Minute Discovery Call', slug: 'discovery-call-30'})
};
fetch('https://api.cal.id/teams/{teamId}/event-types', 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.id/teams/{teamId}/event-types",
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([
'title' => '30 Minute Discovery Call',
'slug' => 'discovery-call-30'
]),
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.cal.id/teams/{teamId}/event-types"
payload := strings.NewReader("{\n \"title\": \"30 Minute Discovery Call\",\n \"slug\": \"discovery-call-30\"\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.cal.id/teams/{teamId}/event-types")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"30 Minute Discovery Call\",\n \"slug\": \"discovery-call-30\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cal.id/teams/{teamId}/event-types")
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 \"title\": \"30 Minute Discovery Call\",\n \"slug\": \"discovery-call-30\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"id": 100933,
"title": "Sales Demo",
"slug": "sales-demo",
"description": "Guided 45 minute product walkthrough with the sales team.",
"interfaceLanguage": "en",
"position": 0,
"locations": [
{
"type": "integrations:daily"
}
],
"bookingFields": [
{
"name": "name",
"type": "name",
"required": true
},
{
"name": "email",
"type": "email",
"required": true
},
{
"name": "notes",
"type": "textarea",
"required": false
}
],
"length": 45,
"offsetStart": 0,
"hidden": false,
"userId": null,
"teamId": 812,
"profileId": null,
"timeZone": null,
"periodType": "UNLIMITED",
"periodStartDate": null,
"periodEndDate": null,
"periodDays": null,
"periodCountCalendarDays": null,
"lockTimeZoneToggleOnBookingPage": false,
"lockedTimeZone": null,
"requiresConfirmation": false,
"requiresConfirmationWillBlockSlot": false,
"requiresConfirmationForFreeEmail": false,
"requiresBookerEmailVerification": false,
"recurringEvent": null,
"disableGuests": false,
"hideCalendarNotes": false,
"hideCalendarEventDetails": false,
"minimumBookingNotice": 120,
"beforeEventBuffer": 0,
"afterEventBuffer": 10,
"seatsPerTimeSlot": null,
"onlyShowFirstAvailableSlot": false,
"disableCancelling": false,
"disableRescheduling": false,
"disableHostCancelling": false,
"disableHostRescheduling": false,
"seatsShowAttendees": null,
"seatsShowAvailabilityCount": null,
"schedulingType": "ROUND_ROBIN",
"scheduleId": 2231,
"price": 0,
"currency": "usd",
"slotInterval": null,
"metadata": {},
"successRedirectUrl": null,
"forwardParamsSuccessRedirect": true,
"bookingLimits": null,
"durationLimits": null,
"isInstantEvent": false,
"instantMeetingExpiryTimeOffsetInSeconds": 90,
"assignAllTeamMembers": false,
"assignRRMembersUsingSegment": false,
"rrSegmentQueryValue": null,
"useEventTypeDestinationCalendarEmail": false,
"isRRWeightsEnabled": false,
"maxLeadThreshold": null,
"includeNoShowInRRCalculation": false,
"allowReschedulingPastBookings": false,
"hideOrganizerEmail": false,
"maxActiveBookingsPerBooker": null,
"maxActiveBookingPerBookerOfferReschedule": false,
"customReplyToEmail": null,
"eventTypeColor": null,
"rescheduleWithSameRoundRobinHost": false,
"secondaryEmailId": null,
"useBookerTimezone": false,
"restrictionScheduleId": null,
"createdDate": "2026-02-11T10:04:18.000Z",
"updatedDate": "2026-04-28T15:31:07.000Z"
},
"message": "Team event type created successfully"
}{
"success": false,
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}
}{
"success": false,
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}
}{
"success": false,
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}
}{
"success": false,
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}
}{
"success": false,
"message": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}
}{
"success": false,
"message": "Rate limit exceeded",
"error": {
"code": "TOO_MANY_REQUESTS",
"message": "Too many requests. Please retry after the rate limit window resets."
}
}Authorizations
Use the Authorization header with Bearer scheme.
Examples:
- API Key: Authorization: Bearer calid_xxxxx
Path Parameters
Numeric team identifier.
x > 142
Body
Human-readable title for the event type shown in booking pages and calendars.
4 - 255"30 Minute Discovery Call"
Unique URL slug for the event type under the owner account.
4 - 100"discovery-call-30"
Optional event type description shown to bookers.
"A short introductory call to discuss your goals and next steps."
Interface language used for the booking experience.
"en"
Sort position used when listing multiple event types.
0
Location or conferencing configuration payload for this event type. Each item type must be one of the supported location type enum values.
Show child attributes
Show child attributes
[ { "type": "attendeeInPerson", "attendeeAddress": "221B Baker Street, London" }, { "type": "inPerson", "address": "OneHash HQ, Bengaluru" }, { "type": "phone", "phone": "+14155551234" }, { "type": "userPhone" }, { "type": "link", "link": "https://meet.example.com/discovery-call" }, { "type": "conferencing", "hostDefault": "integrations:google:meet" }, { "type": "somewhereElse", "somewhereElse": "Client office lobby" } ]
Meeting duration in minutes.
x >= 1030
Offset in minutes added to generated slot start times.
0
Whether the event type should be hidden from public listings.
false
IANA timezone used when event type timezone is locked.
"Asia/Kolkata"
Availability period strategy for booking this event type.
UNLIMITED, ROLLING, ROLLING_WINDOW, RANGE "UNLIMITED"
Start date-time boundary for limited booking windows.
"2026-05-01T00:00:00.000Z"
End date-time boundary for limited booking windows.
"2026-05-31T23:59:59.000Z"
Number of days allowed for period-based booking limits.
30
Whether period day counting should use calendar-day boundaries.
true
Whether new bookings require organizer confirmation.
false
Whether pending confirmation should block the slot from other bookings.
false
Whether confirmation is enforced for bookers using free email domains.
false
Whether the booker must verify email before finalizing booking.
false
Recurring event configuration payload.
{ "count": 4, "frequency": "weekly" }
Whether attendees are prevented from adding guests.
false
Whether internal calendar notes are hidden from attendees.
false
Whether detailed calendar event metadata is hidden from attendees.
false
Minimum notice in minutes required before a slot can be booked.
x >= 0120
Buffer time in minutes blocked before each booking.
0
Buffer time in minutes blocked after each booking.
0
Maximum seats available per slot for seat-based events.
5
Whether only the first available slot should be displayed to bookers.
false
Whether guests are prevented from cancelling bookings.
false
Whether guests are prevented from rescheduling bookings.
false
Whether hosts are prevented from cancelling bookings for team events.
false
Whether hosts are prevented from rescheduling bookings for team events.
false
Whether seat-based events should show attendee details to other attendees.
false
Whether remaining seat availability count should be shown to bookers.
true
Scheduling strategy for owner/team assignment.
ROUND_ROBIN, COLLECTIVE, MANAGED "COLLECTIVE"
Schedule identifier linked to this event type.
1
Price amount in the smallest currency unit for paid events.
0
ISO currency code used when price is set.
"usd"
Custom interval in minutes between suggested slots.
15
Arbitrary metadata payload stored with the event type.
{ "source": "api" }
URL to redirect bookers to after successful booking.
"https://example.com/booking-success"
Whether booking query parameters should be appended to the success redirect URL.
true
Booking limits configuration payload.
{ "PER_DAY": 5 }
Allowed duration limits configuration payload.
{ "minimum": 15, "maximum": 60 }
Whether this event type can be booked instantly for near-immediate meetings.
false
Number of seconds before an instant meeting offer expires.
90
Whether all team members should be assigned for this event type.
false
Whether round-robin members should be selected using a segment query.
false
Segment query payload used for round-robin member assignment.
{ "segment": "enterprise" }
Whether destination calendar email should be derived from event type settings.
false
Whether weighted round-robin distribution is enabled.
false
Maximum lead threshold used for assignment logic.
100
Whether no-show bookings should be included in round-robin calculations.
false
Whether past bookings are eligible for rescheduling.
false
Whether organizer email is hidden from attendees.
false
Maximum number of active bookings allowed per booker.
3
Whether over-limit bookers should be offered reschedule instead of blocking.
false
Custom reply-to email used in booking communications.
"replyto@example.com"
Custom color settings for the event type.
{ "lightEventTypeColor": "#0f172a", "darkEventTypeColor": "#94a3b8" }
Whether reschedules should keep the same round-robin host.
false
Whether booking flow should prioritize the booker timezone.
false
Schedule identifier used to restrict availability for this event type.
2
Full replacement list of user-managed booking fields. Omit to preserve existing fields. Null is not accepted. Internal/system-managed properties such as editable and sources are not accepted.
[ { "name": "company", "type": "text", "label": "Company", "required": false, "placeholder": "Acme Inc." }, { "name": "company_size", "type": "select", "label": "Company size", "required": true, "options": [ { "label": "1-10", "value": "1-10" }, { "label": "11-50", "value": "11-50" } ] } ]
Was this page helpful?