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

# POST /v1/scheduling/appointments

> Create an appointment in pending state with embedded customer data. Keebai generates the id (UUID).

Creates a new appointment. The appointment lands in `pending` state and must be confirmed explicitly with [`POST /appointments/:id/confirm`](/dev/endpoints/scheduling-appointment-confirm). The `_id` is generated by Keebai (UUID v4) and returned in the response.

The customer (`customer`) is embedded in the body — you don't need to create a Customer beforehand. If the service defines `duration_minutes`, you can omit `end_time` and Keebai will compute it from `start_time + duration`.

## Endpoint

```
POST https://api.keebai.com/v1/scheduling/appointments
```

## Required scope

`scheduling:appointments:create`

## Headers

| Header          | Required | Value                    |
| --------------- | -------- | ------------------------ |
| `Authorization` | Yes      | `Bearer kbai_pk_<token>` |
| `Content-Type`  | Yes      | `application/json`       |

## Body

| Field                | Type     | Required | Description                                                           |
| -------------------- | -------- | -------- | --------------------------------------------------------------------- |
| `branch_id`          | `string` | Yes      | Branch where the appointment is booked.                               |
| `service_id`         | `string` | Yes      | Service to deliver.                                                   |
| `professional_id`    | `string` | No       | Assigned professional. If omitted, scheduling picks an available one. |
| `date`               | `string` | Yes      | Date (`YYYY-MM-DD`).                                                  |
| `start_time`         | `string` | Yes      | Start time (`HH:mm`) in the branch's tz.                              |
| `end_time`           | `string` | No       | End time (`HH:mm`). If omitted, computed from the service's duration. |
| `customer.name`      | `string` | Yes      | Customer first name.                                                  |
| `customer.last_name` | `string` | No       | Last name.                                                            |
| `customer.email`     | `string` | No       | Valid email.                                                          |
| `customer.phone`     | `string` | No       | Phone number.                                                         |
| `notes`              | `string` | No       | Internal notes (max 1000 characters).                                 |
| `price`              | `number` | No       | Appointment-specific fee. Overrides the service price.                |
| `currency`           | `string` | No       | ISO 4217 currency (`CLP`, `USD`, etc.).                               |

## Example request

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.keebai.com/v1/scheduling/appointments \
    -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "branch_id": "65a1f2b3c4d5e6f7a8b9c0d1",
      "service_id": "65b1c2d3e4f5a6b7c8d9e0f1",
      "professional_id": "66c1d2e3f4a5b6c7d8e9f0a1",
      "date": "2026-05-02",
      "start_time": "10:00",
      "customer": {
        "name": "Juan",
        "last_name": "Pérez",
        "phone": "+56912345678",
        "email": "juan@example.com"
      },
      "notes": "Primera consulta"
    }'
  ```

  ```js JavaScript theme={null}
  const response = await fetch(
    "https://api.keebai.com/v1/scheduling/appointments",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.KEEBAI_API_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        branch_id: branchId,
        service_id: serviceId,
        professional_id: professionalId,
        date: "2026-05-02",
        start_time: "10:00",
        customer: { name: "Juan", phone: "+56912345678" },
      }),
    },
  );
  const appointment = await response.json();
  ```

  ```python Python theme={null}
  import os, requests

  resp = requests.post(
      "https://api.keebai.com/v1/scheduling/appointments",
      headers={"Authorization": f"Bearer {os.environ['KEEBAI_API_TOKEN']}"},
      json={
          "branch_id": branch_id,
          "service_id": service_id,
          "professional_id": professional_id,
          "date": "2026-05-02",
          "start_time": "10:00",
          "customer": {"name": "Juan", "phone": "+56912345678"},
      },
      timeout=10,
  )
  resp.raise_for_status()
  appointment = resp.json()
  ```
</CodeGroup>

## Response

### 201 Created

Returns the freshly created appointment with its `_id` and `status: "pending"`. Shape identical to the body of [`GET /appointments/:id`](/dev/endpoints/scheduling-appointment-get).

### 400 Bad Request

Invalid body (bad date/time formats, missing fields, malformed email).

### 409 Conflict

The requested slot is already taken or conflicts with another appointment or service rule. The body includes the reason:

```json theme={null}
{
  "error": {
    "code": "SLOT_NOT_AVAILABLE",
    "message": "El profesional no tiene disponibilidad para el horario solicitado."
  }
}
```

### 401 Unauthorized · 403 Forbidden

Same semantics as the rest of the public API.

## Common patterns

### Single-shot booking with immediate confirmation

If your integration validates the appointment in the same step, chain `create` with `confirm`:

```python theme={null}
appointment = requests.post(
    "https://api.keebai.com/v1/scheduling/appointments",
    headers=auth_headers,
    json=payload,
    timeout=10,
).json()

requests.post(
    f"https://api.keebai.com/v1/scheduling/appointments/{appointment['_id']}/confirm",
    headers=auth_headers,
    timeout=10,
).raise_for_status()
```

### Booking with deferred confirmation (customer must confirm)

Leaving the appointment in `pending` lets you implement "confirm your appointment within the next 2 hours" flows (typical when there's an external payment or a human-in-the-loop validation).


## OpenAPI

````yaml POST /v1/scheduling/appointments
openapi: 3.0.0
info:
  title: Keebai Public API
  description: Superficie pública REST autenticada con Personal Access Tokens (PAT).
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.keebai.com
    description: Production
security: []
tags: []
paths:
  /v1/scheduling/appointments:
    post:
      tags:
        - scheduling-appointments
      summary: Crear una cita
      description: Crea una nueva cita en estado pendiente con datos del cliente embebidos.
      operationId: PublicAppointmentsController_create
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublicAppointmentCreateDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAppointmentResponseDto'
      security:
        - PAT: []
components:
  schemas:
    PublicAppointmentCreateDto:
      type: object
      properties:
        branch_id:
          type: string
        service_id:
          type: string
        professional_id:
          type: string
        date:
          type: string
          description: Fecha (YYYY-MM-DD)
        start_time:
          type: string
          description: Hora inicio (HH:mm)
        end_time:
          type: string
          description: >-
            Hora fin (HH:mm). Si se omite, scheduling la calcula desde la
            duración del servicio.
        customer:
          $ref: '#/components/schemas/PublicCustomerDto'
        notes:
          type: string
        price:
          type: number
        currency:
          type: string
      required:
        - branch_id
        - service_id
        - date
        - start_time
        - customer
    PublicAppointmentResponseDto:
      type: object
      properties:
        _id:
          type: string
          description: Id de la cita
        appointment_number:
          type: number
          description: Correlativo legible de la cita
        branch:
          $ref: '#/components/schemas/PublicAppointmentRefDto'
        service:
          $ref: '#/components/schemas/PublicAppointmentRefDto'
        professional:
          $ref: '#/components/schemas/PublicAppointmentProfessionalDto'
        customer:
          $ref: '#/components/schemas/PublicAppointmentCustomerDto'
        dates:
          $ref: '#/components/schemas/PublicAppointmentDatesDto'
        appointment_type:
          type: string
          description: online | in-person
        status:
          type: string
          description: pending | confirmed | cancelled | …
        payment_status:
          type: string
          description: Estado del pago
        notes:
          type: string
        price:
          type: number
        currency:
          type: string
          description: Moneda ISO 4217
        paid:
          type: boolean
        cancelled_at:
          type: string
          format: date-time
        cancellation_reason:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - _id
        - branch
        - dates
        - status
    PublicCustomerDto:
      type: object
      properties:
        name:
          type: string
        last_name:
          type: string
        email:
          type: string
        phone:
          type: string
      required:
        - name
    PublicAppointmentRefDto:
      type: object
      properties:
        id:
          type: string
        display_name:
          type: string
      required:
        - id
    PublicAppointmentProfessionalDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        last_name:
          type: string
        specialty:
          type: string
      required:
        - id
    PublicAppointmentCustomerDto:
      type: object
      properties:
        name:
          type: string
        last_name:
          type: string
        email:
          type: string
        phone:
          type: string
      required:
        - name
    PublicAppointmentDatesDto:
      type: object
      properties:
        start:
          type: string
          format: date-time
        end:
          type: string
          format: date-time
        time_zone:
          type: string
          description: IANA, ej. America/Santiago
      required:
        - start
        - end
  securitySchemes:
    PAT:
      scheme: bearer
      bearerFormat: kbai_pk_<hex>
      type: http
      description: >-
        Personal Access Token con prefijo `kbai_pk_`. Generar desde el portal
        con permiso `developer.manage_tokens`.

````