curl --request POST \
--url https://api.dialtu.com/api/v2/public/calendar/events \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"client": {
"first_name": "Jane",
"last_name": "Roe",
"phone": "+573001112233"
}
}
'import requests
url = "https://api.dialtu.com/api/v2/public/calendar/events"
payload = { "client": {
"first_name": "Jane",
"last_name": "Roe",
"phone": "+573001112233"
} }
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({client: {first_name: 'Jane', last_name: 'Roe', phone: '+573001112233'}})
};
fetch('https://api.dialtu.com/api/v2/public/calendar/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));Book an appointment
Book one appointment.
Everything about the APPOINTMENT is explicit: the service, the professional, the appointment type, both ends of the window, and the timezone they are written in. Nothing is auto-assigned — an unmatched or ambiguous value is refused, and the refusal names what would have been accepted.
Everything about the MESSAGING is not: whether a confirm-request or reminders go out, and when, comes from the calendar’s own Notifications settings, so an appointment booked here behaves exactly like the same appointment booked in the dashboard.
The customer is matched on their phone number and created if unknown.
Availability is never a reason to fail here: the appointment is booked even if it falls outside declared working hours or overlaps another one. The calling system owns the schedule.
curl --request POST \
--url https://api.dialtu.com/api/v2/public/calendar/events \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"client": {
"first_name": "Jane",
"last_name": "Roe",
"phone": "+573001112233"
}
}
'import requests
url = "https://api.dialtu.com/api/v2/public/calendar/events"
payload = { "client": {
"first_name": "Jane",
"last_name": "Roe",
"phone": "+573001112233"
} }
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({client: {first_name: 'Jane', last_name: 'Roe', phone: '+573001112233'}})
};
fetch('https://api.dialtu.com/api/v2/public/calendar/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));Authorizations
API key created in the dashboard under Settings → API Keys. Send it as Authorization: Bearer pk_....
Body
Body of Book an appointment.
The customer the appointment is for.
Show child attributes
Show child attributes
Local start time in timezone, e.g. 2026-09-15T14:00:00. An explicit UTC offset is accepted and then wins over timezone.
Local end time in timezone. Required — it is never derived from the service's default duration.
IANA zone name for start_at/end_at, e.g. 'America/Bogota'.
64Service ID from List bookable services. One of service_id/service_name is required.
Alternative to service_id. One of the two is required.
255Professional ID; must be an active member of the service.
Alternative to professional_id. One of the two is required — this API never picks the professional for you.
255Appointment type ID; must be active and offered by the service.
Alternative to event_type_id. One of the two is required.
255Free text shown to the team in Dialtu.
2000Response
Created
Response of Book an appointment.