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

# Create Booking

> Record an externally-booked appointment so the bot stops asking the contact to book again

## Overview

This endpoint records an appointment that was booked outside of Setter (e.g. directly on your Calendly, a manual phone callback, or any other calendar tool). Once a booking is stored against a contact, the bot will see it on the next inbound message and will not ask the contact to book another appointment or send a booking link.

<Note>
  **Authentication**: All requests require a Bearer token in the Authorization header.
</Note>

## Contact Identification

You must provide either `phone` or `wa_id` to locate the contact. The contact must already exist in Setter — bookings cannot create new contacts. Lookup order:

1. Exact `wa_id` match (if `wa_id` is supplied)
2. `wa_id` derived from `phone` (E.164 minus the leading `+`)
3. `sms_phone_number` match (both with and without leading `+`)

If both `phone` and `wa_id` are provided, `wa_id` is tried first. Pass `default_country` (ISO 3166-1 alpha-2) to disambiguate national-format phone numbers.

## Bot Scope

A booking is only visible to the `bot_id` it was filed against. A bot that runs on multiple channels (e.g. WhatsApp + SMS) sees the same booking on both channels, but a separate bot owned by the same organization will not. Find the `bot_id` in the dashboard URL.

## Idempotency

If `external_id` is provided, the unique key `(organization_id, source, external_id)` makes retries safe. A re-POST with the same triple returns the existing booking with `deduplicated: true` and HTTP `200` instead of creating a duplicate.

## Important Notes

* **Authentication**: All requests require a Bearer token in the Authorization header
* **Phone Format**: Accepted with or without leading `+` and separators (parsed via libphonenumber)
* **Bot Visibility**: Bookings are scoped to the `bot_id` they were filed against
* **Soft Cancellation**: Use [Cancel Booking](/setter-ai/cancel-booking) to remove a booking from the bot's context
* **Time Zone**: Pass an IANA `time_zone` (e.g. `Europe/London`) for correct rendering in the bot's context

## Getting Your API Key

API keys can be generated from your Setter AI dashboard under **Settings > API Keys**. Keep your API key secure and never expose it in client-side code.

## Example Usage

### Create by phone number

```bash theme={null}
curl -X POST https://chat.trysetter.com/api/v1/bookings \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "bot_id": 700,
    "phone": "+447813194799",
    "start_time": "2026-05-25T14:00:00Z",
    "end_time": "2026-05-25T15:00:00Z",
    "time_zone": "Europe/London",
    "source": "calendly",
    "external_id": "calendly-invitee-abc"
  }'
```

### Create by WhatsApp ID

```bash theme={null}
curl -X POST https://chat.trysetter.com/api/v1/bookings \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "bot_id": 700,
    "wa_id": "447813194799",
    "start_time": "2026-05-25T14:00:00Z",
    "time_zone": "Europe/London",
    "source": "manual"
  }'
```


## OpenAPI

````yaml POST /api/v1/bookings
openapi: 3.0.3
info:
  title: Setter AI API
  description: API for Setter AI appointment booking assistant
  version: 1.0.0
  contact:
    email: support@trysetter.com
servers:
  - url: https://chat.trysetter.com
    description: Production server
security:
  - bearerAuth: []
paths:
  /api/v1/bookings:
    post:
      tags:
        - Bookings
      summary: Create Booking
      description: >-
        Record an appointment that was booked outside of Setter (e.g. directly
        on your Calendly, a manual phone callback, or any other calendar tool).
        Once stored against a contact, the bot sees it on the next inbound
        message and will not ask the contact to book another appointment or send
        a booking link.


        **Contact matching** is performed in this order:

        1. Exact `wa_id` match (if `wa_id` is supplied)

        2. `wa_id` derived from `phone` (E.164 minus the leading `+`)

        3. `sms_phone_number` match (both with and without leading `+`)


        The contact must already exist in Setter — bookings cannot create new
        contacts.


        **Bot scope:** a booking is only visible to the `bot_id` it was filed
        against. A bot that runs on multiple channels (e.g. WhatsApp + SMS) sees
        the same booking on both channels, but a separate bot owned by the same
        organization will not.


        **Idempotency:** if `external_id` is provided, the unique key
        `(organization_id, source, external_id)` makes retries safe. A re-POST
        with the same triple returns the existing booking with `deduplicated:
        true` and HTTP `200`.
      operationId: createBooking
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBookingRequest'
            examples:
              by_phone:
                summary: Create by phone number
                value:
                  bot_id: 700
                  phone: '+447813194799'
                  start_time: '2026-05-25T14:00:00Z'
                  end_time: '2026-05-25T15:00:00Z'
                  time_zone: Europe/London
                  source: calendly
                  external_id: calendly-invitee-abc
              by_wa_id:
                summary: Create by WhatsApp ID
                value:
                  bot_id: 700
                  wa_id: '447813194799'
                  start_time: '2026-05-25T14:00:00Z'
                  time_zone: Europe/London
                  source: manual
      responses:
        '200':
          description: >-
            Idempotent replay — a booking with the same `(organization_id,
            source, external_id)` already existed and is returned with
            `deduplicated: true`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateBookingResponse'
              example:
                id: 555
                event_id: 999
                bot_id: 700
                contact_id: 42
                source: calendly
                external_id: calendly-invitee-abc
                start_time: '2026-05-25T14:00:00.000Z'
                end_time: null
                time_zone: Europe/London
                deduplicated: true
        '201':
          description: Booking created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateBookingResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missing_bot_id:
                  value:
                    message: '`bot_id` is required and must be a positive integer'
                missing_identifier:
                  value:
                    message: Either `phone` or `wa_id` is required
                invalid_phone:
                  value:
                    message: '`phone` could not be parsed'
                invalid_start_time:
                  value:
                    message: '`start_time` is not a valid ISO 8601 date'
                invalid_end_time:
                  value:
                    message: '`end_time` must be a valid date after `start_time`'
        '403':
          description: Contact does not belong to your organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                message: Contact does not belong to your organization
        '404':
          description: Bot or contact not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                bot_not_found:
                  value:
                    message: '`bot_id` not found in your organization'
                contact_not_found:
                  value:
                    message: Contact not found for the supplied phone or wa_id
components:
  schemas:
    CreateBookingRequest:
      type: object
      required:
        - bot_id
        - start_time
      properties:
        bot_id:
          type: integer
          description: The bot this booking is for. Find the id in the dashboard URL.
          example: 700
        phone:
          type: string
          description: >-
            Contact's phone number. Accepted with or without leading `+` and
            separators (parsed via libphonenumber). Provide either `phone` or
            `wa_id`.
          example: '+447813194799'
        wa_id:
          type: string
          description: >-
            WhatsApp ID of the contact. Use this instead of `phone` if you have
            it. If both are provided, `wa_id` is tried first.
          example: '447813194799'
        start_time:
          type: string
          format: date-time
          description: ISO 8601 timestamp of the appointment start.
          example: '2026-05-25T14:00:00Z'
        end_time:
          type: string
          format: date-time
          description: >-
            ISO 8601 timestamp of the appointment end. Must be after
            `start_time`.
          example: '2026-05-25T15:00:00Z'
        time_zone:
          type: string
          description: IANA time zone (e.g. `Europe/London`).
          example: Europe/London
        source:
          type: string
          description: Free-form provenance tag. Defaults to `manual`.
          example: calendly
        external_id:
          type: string
          description: >-
            Stable id from your system used to deduplicate retries. Unique key
            is `(organization_id, source, external_id)`.
          example: calendly-invitee-abc
        default_country:
          type: string
          description: >-
            ISO 3166-1 alpha-2 country code (e.g. `GB`). Used to disambiguate
            national-format phone numbers.
          example: GB
        metadata:
          type: object
          description: Arbitrary JSON stored alongside the booking.
          additionalProperties: true
          example:
            invitee_email: lead@example.com
            calendly_event_uri: https://api.calendly.com/scheduled_events/abc
    CreateBookingResponse:
      type: object
      properties:
        id:
          type: integer
          example: 555
        event_id:
          type: integer
          example: 999
        bot_id:
          type: integer
          example: 700
        contact_id:
          type: integer
          example: 42
        source:
          type: string
          example: calendly
        external_id:
          type: string
          nullable: true
          example: calendly-invitee-abc
        start_time:
          type: string
          format: date-time
          example: '2026-05-25T14:00:00.000Z'
        end_time:
          type: string
          format: date-time
          nullable: true
          example: '2026-05-25T15:00:00.000Z'
        time_zone:
          type: string
          nullable: true
          example: Europe/London
        deduplicated:
          type: boolean
          description: >-
            `true` when an `external_id` collision returned the existing record
            instead of creating a new one.
          example: false
    ErrorResponse:
      type: object
      properties:
        message:
          type: string
          description: Error message
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Bearer token authentication using your API key

````