> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dialtu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Book calendar appointments

> Create appointments from an outside system — every field explicit, no surprises

Use the calendar endpoints to book appointments on your Dialtu calendar from n8n, Zapier,
a website form or your own backend, and to check how much room is left before you do:

* **Get bookable capacity per service** — `GET /api/v2/public/calendar/availability`
* **List bookable services** — `GET /api/v2/public/calendar/services`
* **Book an appointment** — `POST /api/v2/public/calendar/events`
* **Waiting lists** — add, list, remove and summarise entries under `/api/v2/public/calendar/waitlist/`

<Note>
  **Every field in a booking is required.** This API has no defaults: it never picks the
  service, the professional or the appointment type for you, never derives the end time from a
  duration, and never sends a notification you did not configure. If a value does not match
  something in your account, the request is refused and the error lists what would have been
  accepted.
</Note>

## Concepts

Four things make up an appointment, and **you supply all four**.

| Thing                | What it is                                                                                                                                   | How you send it                              |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| **Client**           | The customer. **Found by phone number**; created automatically the first time. A name is only needed when the number is new to your account. | `client` object                              |
| **Service**          | What the appointment is *for* ("Consultation", "Haircut").                                                                                   | `service_id` **or** `service_name`           |
| **Professional**     | Who the appointment is *with*.                                                                                                               | `professional_id` **or** `professional_name` |
| **Appointment type** | The *format* (In-Person, Video call, Call).                                                                                                  | `event_type_id` **or** `event_type_name`     |

For each of the last three, send **either** the ID **or** the name. IDs are matched exactly.
Names are matched case- and accent-insensitively, so `consulta medica` finds `Consulta Médica`.
If you send both, the ID is used.

Call **List bookable services** once to see the exact IDs and names your account accepts:

```json theme={null}
{
  "status": "success",
  "data": [
    {
      "id": 12,
      "name": "Consulta Médica",
      "default_duration_minutes": 30,
      "professionals": [ { "id": 4, "name": "Dra. Ana Gómez" }, { "id": 7, "name": "Dr. Luis Peña" } ],
      "event_types": [ { "id": 1, "name": "In-Person" }, { "id": 2, "name": "Video call" } ]
    }
  ]
}
```

## Times and the `timezone` field

Send the local wall-clock time your customer was given, plus the IANA zone it belongs to:

```json theme={null}
{ "start_at": "2026-09-15T14:00:00", "end_at": "2026-09-15T14:30:00", "timezone": "America/Bogota" }
```

You do not have to work out the UTC offset or worry about daylight saving — the rules for that
zone on that date are applied for you. If you prefer, include an explicit offset
(`2026-09-15T09:00:00-05:00`); the offset then wins over `timezone`, which is still required.

`end_at` is required and is never derived from the service's default duration: how long the
appointment runs is your statement, not something the API infers.

## Two things this API does differently

1. **It never refuses on availability.** If the time falls outside working hours or clashes
   with another appointment, it is booked anyway. Your system owns the schedule; Dialtu records
   it. You will not see a `409` on a normal booking.
2. **It does not decide which messages go out.** Whether the customer gets a confirmation
   request or reminders, and how far ahead, comes from the calendar's own settings in Dialtu
   (**Calendar → Settings → Notifications**). An appointment booked here is messaged exactly
   like one booked in the dashboard. There are no fields for it, by design: those messages are
   billed to the Dialtu account, so the account owner chooses them.

## A booking, end to end

```bash theme={null}
curl -X POST https://api.dialtu.com/api/v2/public/calendar/events \
  -H "Authorization: Bearer pk_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "client": { "first_name": "Jane", "last_name": "Roe", "phone": "+573001112233", "email": "jane.roe@example.com" },
    "service_name": "Consulta Médica",
    "professional_name": "Ana Gómez",
    "event_type_name": "In-Person",
    "start_at": "2026-09-15T14:00:00",
    "end_at": "2026-09-15T14:30:00",
    "timezone": "America/Bogota"
  }'
```

```json theme={null}
{
  "status": "success",
  "data": {
    "id": 8811,
    "start_at": "2026-09-15T19:00:00Z",
    "end_at": "2026-09-15T19:30:00Z",
    "status": "booked",
    "source": "api",
    "confirmation_status": "pending",
    "client": { "id": 512, "name": "Jane Roe", "phone": "+573001112233" },
    "calendar_profile": { "calendar_profile_id": 4, "name": "Dra. Ana Gómez" },
    "service": { "id": 12, "name": "Consulta Médica", "color": "#6633ff" },
    "event_type": { "id": 1, "name": "In-Person", "color": "#2b8a3e" },
    "custom_fields": { "booked_by": "api" },
    "created_at": "2026-09-02T18:04:11Z"
  },
  "client_id": 512,
  "client_created": true
}
```

Store `data.id` — it is the appointment's ID in Dialtu. `client_created` tells you whether the
request created the customer (`true`) or matched an existing one (`false`).

## Checking capacity first

**Get bookable capacity per service** answers "how many more appointments fit?" per service
over a date range. `capacity` is summed **per provider**, not unioned by instant — three
providers free at 09:00 is three customers you can call, not one opening — and it is an
**upper bound**: booking one slot can eliminate more than one candidate once buffers interact.
Keep `days` aligned with the search window of the agent that will do the calling, so you do
not queue customers the agent will then tell there is nothing available.

## Wiring it into n8n

<Steps>
  <Step title="HTTP Request node">
    Method `POST`, URL `https://api.dialtu.com/api/v2/public/calendar/events`.
  </Step>

  <Step title="Authentication">
    *Generic Credential Type* → *Header Auth*, name `Authorization`, value `Bearer pk_xxxxxxxx`.
  </Step>

  <Step title="Body">
    **Body Content Type** `JSON`; paste the request body above and replace the values with
    expressions from your earlier nodes. Look the service, professional and appointment-type
    names up once with **List bookable services** and hard-code the IDs — nothing is optional.
  </Step>

  <Step title="On error">
    Read `error_code` from the response body. None of the `400`s are worth retrying unchanged;
    a `500` is. See [Errors](/en/api-reference/errors#calendar--booking).
  </Step>
</Steps>

## Not available yet

Reading, listing, rescheduling and cancelling appointments through the API are not implemented.
Cancel or move an appointment in the Dialtu dashboard. If you delete the appointment upstream,
the Dialtu one stays — cancel it by hand.
