Withlocals Bookings API

Reserve, confirm, amend, cancel, and read bookings.

Operations 5

GET /bookings List bookings #
POST /bookings Create a confirmed booking #
GET /bookings/{bookingId} Get a single booking by id #
PATCH /bookings/{bookingId} Amend a booking (date / time and / or party size) #
DELETE /bookings/{bookingId} Cancel a booking #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/withlocals:withlocals-bookings-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

withlocals-bookings-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Withlocals Partner Bookings API
  version: 1.0.0
  description: 'A single contract for OTA partners and other commercial integrations.

    Covers products, availability, and the create -> amend -> cancel booking

    lifecycle.


    `POST /bookings` creates a `CONFIRMED` booking.'
  contact:
    name: Withlocals Partner Integrations
    email: partners@withlocals.com
  x-logo:
    url: ./assets/logo.svg
    altText: Withlocals
    href: https://www.withlocals.com
    backgroundColor: '#ffffff'
servers:
- url: https://test-api.withlocals.com/v1/partner
  description: Test / Sandbox
security:
- bearerAuth: []
tags:
- name: Bookings
  description: Reserve, confirm, amend, cancel, and read bookings.
paths:
  /bookings:
    get:
      tags:
      - Bookings
      operationId: listBookings
      summary: List bookings
      description: 'Returns bookings matching the supplied filters. Filters combine with

        `AND`. All filters are optional; with none supplied, a default date

        window is applied (roughly the last month through the next year).


        Use `date` for an exact day, or `fromDate` + `toDate` for a range —

        `date` is mutually exclusive with the range pair (supplying both

        returns `400`).


        Possible errors: `BAD_REQUEST`, `UNAUTHORIZED`.'
      parameters:
      - name: partnerReference
        in: query
        required: false
        description: Partner's own booking id.
        schema:
          type: string
        example: XYZ-001
      - name: date
        in: query
        required: false
        description: 'Single booking date (`YYYY-MM-DD`). Mutually exclusive with

          `fromDate` / `toDate`.

          '
        schema:
          type: string
          format: date
        example: '2026-07-15'
      - name: fromDate
        in: query
        required: false
        description: Range start, inclusive. Pair with `toDate`.
        schema:
          type: string
          format: date
        example: '2026-07-01'
      - name: toDate
        in: query
        required: false
        description: Range end, inclusive. Pair with `fromDate`.
        schema:
          type: string
          format: date
        example: '2026-07-31'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Booking'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
      - Bookings
      operationId: createBooking
      summary: Create a confirmed booking
      description: 'Creates a `CONFIRMED` booking. Use `Idempotency-Key` to make retries safe.


        Possible errors: `BAD_REQUEST`, `UNAUTHORIZED`, `NOT_FOUND` (product

        unknown or not bookable), `CONFLICT` (slot no longer available).'
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBookingRequest'
            examples:
              golden:
                $ref: '#/components/examples/create-booking-request'
      responses:
        '201':
          description: Created (`CONFIRMED`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Booking'
              examples:
                golden:
                  $ref: '#/components/examples/booking-confirmed'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
  /bookings/{bookingId}:
    get:
      tags:
      - Bookings
      operationId: getBooking
      summary: Get a single booking by id
      description: 'Reads a single booking by its Withlocals id. Ownership-scoped: a partner

        can only fetch bookings belonging to its own company. A booking owned by

        another partner returns `404` (not `403`), so existence is not revealed.


        Possible errors: `NOT_FOUND`, `UNAUTHORIZED`.'
      parameters:
      - $ref: '#/components/parameters/BookingId'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Booking'
              examples:
                confirmed:
                  $ref: '#/components/examples/booking-confirmed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
      - Bookings
      operationId: amendBooking
      summary: Amend a booking (date / time and / or party size)
      description: 'Partial update. May trigger an

        internal host transfer if the original host is unavailable on the new date.


        Possible errors: `BAD_REQUEST`, `NOT_FOUND`, `PRECONDITION_FAILED` (past cut-off), `UNAUTHORIZED`.'
      parameters:
      - $ref: '#/components/parameters/BookingId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AmendRequest'
            examples:
              golden:
                $ref: '#/components/examples/amend-request'
      responses:
        '200':
          description: Amended
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Booking'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
    delete:
      tags:
      - Bookings
      operationId: cancelBooking
      summary: Cancel a booking
      description: 'Cancels a booking. The booking **persists** with `status=CANCELLED` and a

        `cancellationReason` of `CANCELLEDBYGUEST` (partner-initiated cancellations

        are always attributed to the guest); a subsequent `GET` still returns it.


        The cancellation `reason` may be sent in the request body, or as the

        `reason` query parameter for clients that cannot send `DELETE` bodies. When

        supplied, it must be one of the `GuestCancellationReason` values, otherwise

        the request returns `400`.


        Possible errors: `BAD_REQUEST` (invalid `reason`), `NOT_FOUND`, `UNAUTHORIZED`.'
      parameters:
      - $ref: '#/components/parameters/BookingId'
      - name: reason
        in: query
        required: false
        description: Fallback for clients that cannot send a `DELETE` body.
        schema:
          $ref: '#/components/schemas/GuestCancellationReason'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CancelRequest'
      responses:
        '200':
          description: Cancelled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Booking'
              examples:
                golden:
                  $ref: '#/components/examples/cancel-response-cancelled'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  schemas:
    GuestCancellationReason:
      type: string
      description: 'Why the guest is cancelling. Sent on partner-initiated cancellations

        (`DELETE /bookings/{bookingId}`).


        | Value | Meaning |

        |---|---|

        | `MY_PLANS_CHANGED`    | My plans changed |

        | `FOUND_BETTER_OPTION` | Found better option |

        | `HOST_ASKED_TO_CANCEL`| Host asked to cancel |

        | `HOST_NOT_RESPONDING` | Host not responding |

        | `OTHER`               | Other |

        '
      enum:
      - MY_PLANS_CHANGED
      - FOUND_BETTER_OPTION
      - HOST_ASKED_TO_CANCEL
      - HOST_NOT_RESPONDING
      - OTHER
      example: MY_PLANS_CHANGED
    BookingStatus:
      type: string
      description: 'Partner-facing booking lifecycle. Translated from the internal `TripStatus`

        by a single mapper (`PartnerBookingStatus.fromTripStatus`).


        v1 lifecycle (no holds — `POST /bookings` creates a CONFIRMED booking

        directly):


        | Partner status | From internal `TripStatus` |

        |---|---|

        | `CONFIRMED`  | `CONFIRMED`, `COMPLETED` |

        | `CANCELLED`  | `CANCELLEDBYGUEST` / `HOST` / `ADMIN` / `HOSTNOSHOW` / `COUPON` (with `cancellationReason`) |


        Internal statuses that belong to the guest negotiation flow (`PROPOSAL`,

        `PAIDPROPOSAL`, `UNCONFIRMED`, `REJECTED`, `DELETED`) are not

        partner-visible. `RESERVATION` / `TIMEDOUT` / `EXPIRED` cannot occur in

        v1 because there is no reserve step.

        '
      enum:
      - CONFIRMED
      - CANCELLED
    CreateBookingRequest:
      type: object
      description: 'Body of `POST /bookings` — creates a `CONFIRMED` booking.

        '
      required:
      - productId
      - date
      - time
      - numberOfAdults
      - mainGuest
      properties:
        productId:
          type: string
          format: uuid
        date:
          type: string
          format: date
          example: '2026-07-15'
        time:
          type: string
          pattern: ^[0-2][0-9]:[0-5][0-9]$
          example: '10:00'
        numberOfAdults:
          type: integer
          minimum: 1
          example: 2
        numberOfChildren:
          type: integer
          minimum: 0
          default: 0
        mainGuest:
          $ref: '#/components/schemas/Guest'
        otherGuests:
          type: array
          items:
            $ref: '#/components/schemas/Guest'
        partnerReference:
          type: string
          description: Partner's own booking id.
          example: XYZ-001
        specialRequest:
          type: string
          description: Free-text note from the guest, forwarded to the host.
        tourLanguage:
          type: string
          description: ISO-639-1 language code.
          example: en
    Guest:
      type: object
      description: A single guest on a booking.
      required:
      - firstName
      properties:
        firstName:
          type: string
          example: Anna
        lastName:
          type: string
          example: Kowalska
        email:
          type: string
          format: email
          example: anna@example.com
        phone:
          type: string
          example: '+31201234567'
    AmendRequest:
      type: object
      description: 'Body of `PATCH /bookings/{bookingId}`. Any subset of fields may be supplied.

        '
      properties:
        date:
          type: string
          format: date
        time:
          type: string
          pattern: ^[0-2][0-9]:[0-5][0-9]$
        numberOfAdults:
          type: integer
          minimum: 1
        numberOfChildren:
          type: integer
          minimum: 0
    Error:
      type: object
      description: 'Error envelope.

        '
      required:
      - error
      - errorMessage
      properties:
        error:
          type: string
          description: Stable error code. Partners are expected to switch on this value.
          enum:
          - BAD_REQUEST
          - UNAUTHORIZED
          - FORBIDDEN
          - NOT_FOUND
          - CONFLICT
          - PRECONDITION_FAILED
          - INTERNAL_ERROR
        errorMessage:
          type: string
          description: Human-readable message. Not stable; do not parse.
          example: Hold expired before confirmation.
        requestId:
          type: string
          description: Trace id for support requests.
          example: req_5f3a9b71
    CancelRequest:
      type: object
      description: Optional body of `DELETE /bookings/{bookingId}`.
      properties:
        reason:
          $ref: '#/components/schemas/GuestCancellationReason'
    MeetingPoint:
      type: object
      description: Where the experience starts.
      required:
      - name
      properties:
        name:
          type: string
          description: Short human-readable name.
          example: Hyakumanben Chion-ji Temple
        address:
          type: string
          description: Formatted address line.
          example: Japan, 〒600-8012 Kyoto, Shimogyo Ward, 四条大橋西詰
        lat:
          type: number
          format: double
          description: Latitude in decimal degrees (WGS84).
          example: 35.0298797
        lon:
          type: number
          format: double
          description: Longitude in decimal degrees (WGS84).
          example: 135.7807599
        instructions:
          type: string
          description: Free-text guidance for finding the meeting spot.
    Booking:
      type: object
      description: 'A partner booking. Returned by create, amend, cancel, and read.


        When `status=CANCELLED`, `cancellationReason` is set and the record persists

        for partner refund handling.

        '
      required:
      - id
      - status
      - productId
      - title
      - date
      - time
      - timeZone
      - createdAt
      properties:
        id:
          type: string
          format: uuid
          description: Withlocals booking id.
        partnerReference:
          type: string
          description: Partner's own booking id (internally `external_id`).
          example: XYZ-001
        productId:
          type: string
          format: uuid
        title:
          type: string
          description: Denormalized product title at the time of booking.
          example: A Relaxed Morning at Hyakumanben Craft Market
        date:
          type: string
          format: date
          example: '2027-10-15'
        time:
          type: string
          pattern: ^[0-2][0-9]:[0-5][0-9]$
          description: Local clock time at the experience location.
          example: 09:00
        timeZone:
          type: string
          description: IANA time zone for `date` / `time`.
          example: Asia/Tokyo
        status:
          $ref: '#/components/schemas/BookingStatus'
        cancellationReason:
          type: string
          enum:
          - CANCELLEDBYGUEST
          - CANCELLEDBYHOST
          - CANCELLEDBYADMIN
          - HOSTNOSHOW
          description: 'Present when `status=CANCELLED`. Partner-initiated cancellations

            (`DELETE /bookings/{bookingId}`) always set `CANCELLEDBYGUEST`; the

            other values appear on bookings cancelled internally by the host,

            admin, or marked as a no-show.

            '
        cancellationDeadline:
          type: string
          format: date-time
          description: Latest moment the booking can be cancelled for a full refund.
          example: '2027-10-08T09:00:00Z'
        createdAt:
          type: string
          format: date-time
          description: When the booking was created.
          example: '2026-05-24T17:11:17Z'
        meetingPoint:
          $ref: '#/components/schemas/MeetingPoint'
        tourLanguage:
          type: string
          description: ISO-639-1 language code.
          example: en
        specialRequest:
          type: string
          description: Free-text note from the guest, forwarded to the host.
        mainGuest:
          type: object
          required:
          - firstName
          description: 'The main guest (traveller) on the booking. This is the lead traveller

            supplied as `mainGuest` on create. Absent when no main guest is recorded.

            '
          properties:
            firstName:
              type: string
              example: Maria
            lastName:
              type: string
              example: Rossi
            phoneNumber:
              type: string
              description: Absent when the main guest has no phone number on their profile.
              example: '+31201234567'
        host:
          type: object
          required:
          - firstName
          description: Minimal host details for day-of identification.
          properties:
            firstName:
              type: string
              example: Ren
            phoneNumber:
              type: string
              description: Absent when the host has no phone number on their profile.
              example: '+31201234567'
  examples:
    booking-confirmed:
      summary: CONFIRMED booking returned by POST /bookings
      value:
        id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
        partnerReference: XYZ-001
        productId: 1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed
        title: Hidden food gems of Amsterdam
        date: '2026-07-15'
        time: '10:00'
        timeZone: Europe/Amsterdam
        status: CONFIRMED
        cancellationDeadline: '2026-07-08T08:00:00Z'
        createdAt: '2026-05-28T13:42:11Z'
        meetingPoint:
          name: Café Brecht
          address: Weteringschans 157, 1017 SE Amsterdam, Netherlands
          lat: 52.3617
          lon: 4.8907
        tourLanguage: en
        mainGuest:
          firstName: Anna
          lastName: Jansen
          phoneNumber: '+31201234567'
        host:
          firstName: Carla
          phoneNumber: '+31209876543'
    amend-request:
      summary: Reschedule one day later, same start time
      value:
        date: '2026-07-16'
        time: '10:00'
    create-booking-request:
      summary: Book 2 adults at 10:00 on 15 Jul 2026
      value:
        productId: 1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed
        date: '2026-07-15'
        time: '10:00'
        numberOfAdults: 2
        numberOfChildren: 0
        mainGuest:
          firstName: Anna
          lastName: Kowalska
          email: anna@example.com
          phone: '+31201234567'
        partnerReference: XYZ-001
        tourLanguage: en
    cancel-response-cancelled:
      summary: Cancelled booking (persists with reason)
      value:
        id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
        partnerReference: XYZ-001
        productId: 1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed
        title: Hidden food gems of Amsterdam
        date: '2026-07-15'
        time: '10:00'
        timeZone: Europe/Amsterdam
        status: CANCELLED
        cancellationReason: CANCELLEDBYGUEST
        createdAt: '2026-05-28T13:42:11Z'
  parameters:
    BookingId:
      name: bookingId
      in: path
      required: true
      description: Withlocals booking id (returned by `POST /bookings`).
      schema:
        type: string
        format: uuid
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: 'Client-generated key making the request safe to retry. Repeated requests with

        the same key return the original response.

        '
      schema:
        type: string
        maxLength: 255
  responses:
    Conflict:
      description: 'Conflicting state. Common causes: the hold has expired, the booking has

        already been confirmed or cancelled, or a concurrent change occurred.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: CONFLICT
            errorMessage: Hold expired before confirmation.
            requestId: req_7c2b18d5
    Unauthorized:
      description: Missing or invalid Bearer token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: UNAUTHORIZED
            errorMessage: Missing or invalid API token
            requestId: req_7c2b18d1
    BadRequest:
      description: The request is malformed or fails validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: BAD_REQUEST
            errorMessage: '`date` is required.'
            requestId: req_7c2b18d2
    NotFound:
      description: The resource does not exist or is not visible to this partner.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: NOT_FOUND
            errorMessage: No product with that id.
            requestId: req_7c2b18d3
    PreconditionFailed:
      description: 'Precondition for the action is not met (e.g. the booking is past its

        amendment cut-off).

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: PRECONDITION_FAILED
            errorMessage: Booking is no longer amendable (cut-off passed).
            requestId: req_7c2b18d6
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: opaque
      description: "Per-partner opaque API token issued by Withlocals. Send on every\nrequest as:\n\n    Authorization: Bearer <token>\n"