---
openapi: 3.0.1
info:
  title: Service Public API
  version: '2026-06-27'
  description: 'Public, read-only reservation & guest API served on api.useservice.app.
    Authenticate with a restaurant API key as `Authorization: Bearer sk_live_...`.'
servers:
- url: https://api.useservice.app
  description: Production
paths:
  "/v1/guests":
    get:
      summary: List guests
      tags:
      - Guests
      security:
      - api_key: []
      operationId: listGuests
      parameters:
      - name: vip
        in: query
        required: false
        schema:
          type: boolean
      - name: blacklisted
        in: query
        required: false
        schema:
          type: boolean
      - name: phone
        in: query
        required: false
        description: Normalized to E.164 before matching
        schema:
          type: string
      - name: email
        in: query
        required: false
        schema:
          type: string
      - name: query
        in: query
        required: false
        description: Substring match on name/email
        schema:
          type: string
      - name: updated_since
        in: query
        required: false
        schema:
          type: string
      - name: limit
        in: query
        required: false
        schema:
          type: integer
      - name: starting_after
        in: query
        required: false
        schema:
          type: string
      - name: ending_before
        in: query
        required: false
        schema:
          type: string
      responses:
        '200':
          description: guests listed
          headers:
            Service-Version:
              schema:
                type: string
              description: Dated API version applied to this response
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    enum:
                    - list
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/Guest"
                  has_more:
                    type: boolean
                required:
                - object
                - data
                - has_more
        '401':
          description: missing or invalid API key
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
  "/v1/guests/{id}":
    get:
      summary: Retrieve a guest
      tags:
      - Guests
      security:
      - api_key: []
      operationId: getGuest
      parameters:
      - name: id
        in: path
        description: Guest public id (gst_…)
        required: true
        schema:
          type: string
      responses:
        '200':
          description: guest found
          headers:
            Service-Version:
              schema:
                type: string
              description: Dated API version applied to this response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Guest"
        '404':
          description: no such guest
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
  "/openapi.yaml":
    get:
      summary: Download the public OpenAPI spec (YAML)
      tags:
      - Meta
      operationId: getOpenapiSpecYaml
      responses:
        '200':
          description: OpenAPI spec, in YAML
          headers:
            Cache-Control:
              schema:
                type: string
              description: Public, short-lived cache
  "/openapi.json":
    get:
      summary: Download the public OpenAPI spec (JSON)
      tags:
      - Meta
      operationId: getOpenapiSpecJson
      responses:
        '200':
          description: OpenAPI spec, in JSON
          headers:
            Cache-Control:
              schema:
                type: string
              description: Public, short-lived cache
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
  "/v1/reservations":
    get:
      summary: List reservations
      tags:
      - Reservations
      security:
      - api_key: []
      operationId: listReservations
      parameters:
      - name: status
        in: query
        required: false
        description: Filter by status
        schema:
          type: string
      - name: source
        in: query
        required: false
        schema:
          type: string
      - name: guest_id
        in: query
        required: false
        description: Filter by guest public id (gst_…)
        schema:
          type: string
      - name: updated_since
        in: query
        required: false
        description: ISO-8601; switches sort to (updated_at asc, id asc)
        schema:
          type: string
      - name: limit
        in: query
        required: false
        schema:
          type: integer
      - name: starting_after
        in: query
        required: false
        schema:
          type: string
      - name: ending_before
        in: query
        required: false
        schema:
          type: string
      - name: expand
        in: query
        required: false
        description: expand=guest inlines the full guest object
        schema:
          type: string
      responses:
        '200':
          description: reservations listed
          headers:
            Service-Version:
              schema:
                type: string
              description: Dated API version applied to this response
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    enum:
                    - list
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/Reservation"
                  has_more:
                    type: boolean
                required:
                - object
                - data
                - has_more
        '400':
          description: invalid request parameter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
        '401':
          description: missing or invalid API key
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
        '429':
          description: rate limit exceeded
          headers:
            RateLimit-Limit:
              schema:
                type: integer
              description: Requests permitted in the window
            RateLimit-Remaining:
              schema:
                type: integer
              description: Requests remaining in the window
            RateLimit-Reset:
              schema:
                type: integer
              description: Seconds until the bucket refills
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
  "/v1/reservations/{id}":
    get:
      summary: Retrieve a reservation
      tags:
      - Reservations
      security:
      - api_key: []
      operationId: getReservation
      parameters:
      - name: id
        in: path
        description: Reservation public id (resv_…)
        required: true
        schema:
          type: string
      - name: expand
        in: query
        required: false
        schema:
          type: string
      responses:
        '200':
          description: reservation found
          headers:
            Service-Version:
              schema:
                type: string
              description: Dated API version applied to this response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Reservation"
        '404':
          description: no such reservation
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
  "/v1/reservations/{id}/events":
    get:
      summary: List reservation lifecycle events
      tags:
      - Reservations
      security:
      - api_key: []
      operationId: listReservationEvents
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: limit
        in: query
        required: false
        schema:
          type: integer
      - name: starting_after
        in: query
        required: false
        schema:
          type: string
      responses:
        '200':
          description: events listed
          headers:
            Service-Version:
              schema:
                type: string
              description: Dated API version applied to this response
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    enum:
                    - list
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/ReservationEvent"
                  has_more:
                    type: boolean
                required:
                - object
                - data
                - has_more
        '404':
          description: no such reservation
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
components:
  securitySchemes:
    api_key:
      type: http
      scheme: bearer
      description: 'Restaurant API key: `Authorization: Bearer sk_live_...`'
  schemas:
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            type:
              type: string
              enum:
              - invalid_request_error
              - authentication_error
              - rate_limit_error
              - api_error
            code:
              type: string
            message:
              type: string
            param:
              type: string
              nullable: true
            doc_url:
              type: string
              nullable: true
          required:
          - type
          - code
          - message
      required:
      - error
    Section:
      type: object
      properties:
        object:
          type: string
          enum:
          - section
        id:
          type: string
        name:
          type: string
        area_type:
          type: string
          enum:
          - indoor
          - outdoor
          - terrace
          - bar
          - lounge
          - private_room
          - patio
          - rooftop
      required:
      - object
      - id
      - name
      - area_type
    Table:
      type: object
      properties:
        object:
          type: string
          enum:
          - table
        id:
          type: string
        name:
          type: string
        min_capacity:
          type: integer
          nullable: true
        max_capacity:
          type: integer
          nullable: true
        section:
          type: object
          properties:
            object:
              type: string
              enum:
              - section
            id:
              type: string
            name:
              type: string
            area_type:
              type: string
              enum:
              - indoor
              - outdoor
              - terrace
              - bar
              - lounge
              - private_room
              - patio
              - rooftop
          required:
          - object
          - id
          - name
          - area_type
          nullable: true
      required:
      - object
      - id
      - name
      - min_capacity
      - max_capacity
      - section
    Guest:
      type: object
      properties:
        object:
          type: string
          enum:
          - guest
        id:
          type: string
        first_name:
          type: string
          nullable: true
        last_name:
          type: string
        email:
          type: string
          nullable: true
        phone:
          type: string
          nullable: true
        notes:
          type: string
          nullable: true
        blacklisted:
          type: boolean
        blacklist_reason:
          type: string
          nullable: true
        dietary_preferences:
          type: array
          items:
            type: string
        allergies:
          type: array
          items:
            type: string
        birthday:
          type: string
          format: date
          nullable: true
        anniversary:
          type: string
          format: date
          nullable: true
        vip:
          type: boolean
        language:
          type: string
          nullable: true
          enum:
          - fr
          - en
          - de
          - es
          - it
          - nl
          - pt
          - da
          - sv
          - 'no'
          - fi
        source:
          type: string
          enum:
          - manual
          - widget
          - import
        marketing_email_consent:
          type: boolean
        marketing_email_consent_at:
          type: string
          format: date-time
          nullable: true
        marketing_sms_consent:
          type: boolean
        marketing_sms_consent_at:
          type: string
          format: date-time
          nullable: true
        visit_count:
          type: integer
        no_show_count:
          type: integer
        cancellation_count:
          type: integer
        first_visit_at:
          type: string
          format: date-time
          nullable: true
        last_visit_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
      - object
      - id
      - last_name
      - blacklisted
      - dietary_preferences
      - allergies
      - vip
      - source
      - marketing_email_consent
      - marketing_sms_consent
      - visit_count
      - no_show_count
      - cancellation_count
      - created_at
      - updated_at
    Reservation:
      type: object
      properties:
        object:
          type: string
          enum:
          - reservation
        id:
          type: string
        status:
          type: string
          enum:
          - pending
          - confirmed
          - partially_seated
          - seated
          - late
          - completed
          - cancelled
          - no_show
        source:
          type: string
          enum:
          - manual
          - widget
          - phone
          - walk_in
          - import
        booking_channel:
          type: string
          enum:
          - back_office
          - website_widget
          - phone
          - walk_in
          - google_reserve
        party_size:
          type: integer
        actual_party_size:
          type: integer
          nullable: true
        service_date:
          type: string
          format: date
        starts_at:
          type: string
          format: date-time
        ends_at:
          type: string
          format: date-time
          nullable: true
        guest_notes:
          type: string
          nullable: true
        internal_notes:
          type: string
          nullable: true
        guest:
          nullable: true
          anyOf:
          - type: string
          - "$ref": "#/components/schemas/Guest"
        tables:
          type: array
          items:
            "$ref": "#/components/schemas/Table"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
      - object
      - id
      - status
      - source
      - booking_channel
      - party_size
      - service_date
      - starts_at
      - tables
      - created_at
      - updated_at
    ReservationEvent:
      type: object
      properties:
        object:
          type: string
          enum:
          - reservation_event
        id:
          type: string
        type:
          type: string
          enum:
          - reservation.created
          - reservation.confirmed
          - reservation.declined
          - reservation.updated
          - reservation.pending
          - reservation.partially_seated
          - reservation.seated
          - reservation.completed
          - reservation.cancelled
          - reservation.no_show
        created_at:
          type: string
          format: date-time
        data:
          type: object
          additionalProperties: true
      required:
      - object
      - id
      - type
      - created_at
      - data
