# Schedule meetings

Your organization has agendas: each one has its opening hours, the length of a meeting and the people who attend. The dashboard configures them (Mi Negocio, Agendamiento) and each person connects their calendar. The API lets your own screen or system read the free slots, book one and follow the meeting afterwards. Meetings booked by an agent or by your team in the dashboard show up here too.

## 1. Pick an agenda

```bash title="cURL"
curl https://api.lelemon.cl/v2/scheduling/agendas \
  -H "Authorization: Bearer $LELEMON_API_KEY"
```


Each agenda has an `id` (`agd_…`), its `timezone`, `slotDurationMinutes`, `minNoticeHours` and `maxDaysAhead`. Routes take the id, never the slug. A paused agenda is not listed and answers `404`. An empty list means the organization has not set up scheduling yet. Needs `scheduling:read`.

## 2. Read the free slots

```bash title="cURL"
curl "https://api.lelemon.cl/v2/scheduling/availability?agendaId=agd_5RvP2aKq9XwLmN3tYbZc01&daysAhead=7" \
  -H "Authorization: Bearer $LELEMON_API_KEY"
```


The answer lists `slots` with `start` and `end`, nearest first. It crosses the opening hours with the calendars of the people in the agenda, the buffers and the minimum notice. It does not say whose calendar a slot comes from or what fills the busy ones. `daysAhead` is 7 by default and up to 60, and the `maxDaysAhead` of the agenda still caps it: a number above 60 is `400`, not a silent cut.

## 3. Book a slot

```bash title="cURL"
curl "https://api.lelemon.cl/v2/scheduling/meetings?dryRun=true" \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  -H "Idempotency-Key: booking-88123-1" \
  --json '{
    "agendaId": "agd_5RvP2aKq9XwLmN3tYbZc01",
    "start": "2026-11-03T13:00:00.000Z",
    "attendeeName": "Ana Pérez",
    "attendeeEmail": "ana@empresa.cl",
    "context": "Wants a quote for two stores"
  }'
```


Booking creates the event in the calendar of the person the agenda assigns and sends the invitation, with the video call link, to `attendeeEmail`. It cannot be undone, only canceled, so `dryRun=true` runs the same checks (the agenda, the [recipient allowlist](/guides/best-practices#limit-who-can-receive-messages), that `start` is a free slot) and answers `200` with the meeting as it would be created, booking and sending nothing. Without `dryRun` the answer is `201` with the meeting: its `id` (`mtg_…`), `start`, `end`, `meetLink` and `status: "scheduled"`. Needs `scheduling:write`.

Send an `Idempotency-Key`: repeating the request with the same key returns the same meeting and books once. `start` must be the start of a slot of the availability, with a time zone.

`409 slot_not_available` means someone took the slot between your read and your booking, or `start` is not one of the slots. Read the availability again and pick another one: repeating the same `start` will not work.

## 4. Follow, move and cancel

- `GET /v2/scheduling/meetings` lists the meetings of the organization, newest booked first, with the filters `agendaId`, `status`, `attendeeEmail`, `startsFrom` and `startsTo`. `GET /v2/scheduling/meetings/{id}` reads one. They carry the name, email and phone of the attendee.
- `POST /v2/scheduling/meetings/{id}/reschedule` with a new `start` moves the same meeting, with the same person assigned, and updates the calendar event, which tells the attendee. The new `start` must be a free slot and the person assigned must be free then. `dryRun=true` checks it and moves nothing.
- `POST /v2/scheduling/meetings/{id}/cancel` deletes the event, which tells the attendee, frees the slot and stores the optional `reason`. The meeting stays readable with `status: "canceled"`.

A meeting that is already canceled, completed or a no-show answers `409 meeting_not_active` to both.

## Events

Webhook endpoints can listen to `meeting.scheduled`, `meeting.rescheduled` and `meeting.canceled`. They fire for every meeting of the organization, whoever created it: your API call, the dashboard or an agent. Each one carries the `meeting`. See [Receive events](/webhooks/receive).

## Subaccounts

With the `Lelemon-Subaccount` header the request acts on the agendas and meetings of that subaccount, and never on those of the platform account.

## Set a team member as available or away

The assignment of new cases gives them to people who are available. Your system can read and set that state, for example when someone leaves their shift.

```bash title="cURL"
curl -X PUT https://api.lelemon.cl/v2/members/mbr_4Zq0x8bVw1TqS9mYp3RkLd/availability \
  -H "Authorization: Bearer $LELEMON_API_KEY" \
  --json '{ "status": "away" }'
```


Find the `mbr_` id with `GET /v2/members`. `GET /v2/members/{id}/availability` answers `status` (what was marked) and `availableForAssignment` (whether the assignment would give them a case now). The two differ when the organization has opening hours and they are closed: the automatic away by schedule wins over `available`, unless the person asked to keep going a while longer (`extendedUntil` in the future). Setting `away` also drops that extension.

It does what the person does when they change their own status, and setting the status they already have writes nothing, so repeating the request is safe. Reading needs `members:read`; setting needs `members:write`, which `members:read` does not include. The v1 scopes `miembros:read` and `miembros:write` only open the v1 routes.
