{
  "openapi": "3.1.0",
  "info": {
    "title": "Tripistic API",
    "version": "2.0.0",
    "summary": "REST API for the Tripistic travel operations platform.",
    "description": "Bookings, tours, availability, customers, and public booking endpoints. All authenticated resources are scoped to a workspace. See https://tripistic.com/developers for guides.",
    "contact": {
      "name": "Tripistic Support",
      "email": "support@tripistic.com",
      "url": "https://tripistic.com/developers"
    },
    "license": { "name": "Proprietary", "url": "https://tripistic.com/legal/license-agreement" }
  },
  "servers": [{ "url": "https://tripistic.com/api", "description": "Production" }],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Bookings", "description": "Reservation records and their lifecycle" },
    { "name": "Tours", "description": "Product catalog" },
    { "name": "Availability", "description": "Bookable departures" },
    { "name": "Customers", "description": "CRM records" },
    { "name": "Public", "description": "Unauthenticated endpoints for booking widgets" }
  ],
  "paths": {
    "/workspaces/{workspaceId}/bookings": {
      "parameters": [{ "$ref": "#/components/parameters/workspaceId" }],
      "get": {
        "tags": ["Bookings"],
        "operationId": "listBookings",
        "summary": "List bookings",
        "parameters": [
          { "$ref": "#/components/parameters/limit" },
          { "$ref": "#/components/parameters/cursor" },
          { "$ref": "#/components/parameters/from" },
          { "$ref": "#/components/parameters/to" },
          {
            "name": "status",
            "in": "query",
            "schema": { "$ref": "#/components/schemas/BookingStatus" }
          },
          {
            "name": "paymentStatus",
            "in": "query",
            "schema": { "$ref": "#/components/schemas/PaymentStatus" }
          },
          { "name": "sort", "in": "query", "schema": { "type": "string", "example": "-createdAt" } }
        ],
        "responses": {
          "200": {
            "description": "A page of bookings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data", "pagination"],
                  "properties": {
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Booking" } },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthenticated" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "tags": ["Bookings"],
        "operationId": "createBooking",
        "summary": "Create a booking",
        "description": "Reserves seats atomically. Returns 409 when capacity is exhausted.",
        "parameters": [{ "$ref": "#/components/parameters/idempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": { "schema": { "$ref": "#/components/schemas/BookingCreate" } }
          }
        },
        "responses": {
          "201": { "$ref": "#/components/responses/BookingEnvelope" },
          "400": { "$ref": "#/components/responses/ValidationError" },
          "401": { "$ref": "#/components/responses/Unauthenticated" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/workspaces/{workspaceId}/bookings/{bookingId}": {
      "parameters": [
        { "$ref": "#/components/parameters/workspaceId" },
        { "$ref": "#/components/parameters/bookingId" }
      ],
      "get": {
        "tags": ["Bookings"],
        "operationId": "getBooking",
        "summary": "Retrieve a booking",
        "responses": {
          "200": { "$ref": "#/components/responses/BookingEnvelope" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "patch": {
        "tags": ["Bookings"],
        "operationId": "updateBooking",
        "summary": "Update a booking",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "availabilityId": { "type": "string" },
                  "participants": {
                    "type": "array",
                    "items": { "$ref": "#/components/schemas/ParticipantInput" }
                  },
                  "notes": { "type": "string" },
                  "totalAmount": { "type": "integer" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/BookingEnvelope" },
          "409": { "$ref": "#/components/responses/Conflict" }
        }
      }
    },
    "/workspaces/{workspaceId}/bookings/{bookingId}/status": {
      "parameters": [
        { "$ref": "#/components/parameters/workspaceId" },
        { "$ref": "#/components/parameters/bookingId" }
      ],
      "post": {
        "tags": ["Bookings"],
        "operationId": "setBookingStatus",
        "summary": "Change booking status",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["status"],
                "properties": {
                  "status": { "$ref": "#/components/schemas/BookingStatus" },
                  "reason": { "type": "string" },
                  "notifyCustomer": { "type": "boolean", "default": false }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/BookingEnvelope" },
          "422": { "$ref": "#/components/responses/Unprocessable" }
        }
      }
    },
    "/workspaces/{workspaceId}/bookings/{bookingId}/payment-link": {
      "parameters": [
        { "$ref": "#/components/parameters/workspaceId" },
        { "$ref": "#/components/parameters/bookingId" }
      ],
      "post": {
        "tags": ["Bookings"],
        "operationId": "createPaymentLink",
        "summary": "Create a payment link for the outstanding balance",
        "responses": {
          "201": {
            "description": "Payment link created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "url": { "type": "string", "format": "uri" },
                        "amount": { "type": "integer" },
                        "expiresAt": { "type": "string", "format": "date-time" }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/{workspaceId}/tours": {
      "parameters": [{ "$ref": "#/components/parameters/workspaceId" }],
      "get": {
        "tags": ["Tours"],
        "operationId": "listTours",
        "summary": "List tours",
        "parameters": [
          { "$ref": "#/components/parameters/limit" },
          { "$ref": "#/components/parameters/cursor" }
        ],
        "responses": {
          "200": {
            "description": "A page of tours",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Tour" } },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/{workspaceId}/availability": {
      "parameters": [{ "$ref": "#/components/parameters/workspaceId" }],
      "get": {
        "tags": ["Availability"],
        "operationId": "listAvailability",
        "summary": "List departures",
        "parameters": [
          { "$ref": "#/components/parameters/from" },
          { "$ref": "#/components/parameters/to" },
          { "name": "tourId", "in": "query", "schema": { "type": "string" } },
          { "$ref": "#/components/parameters/limit" },
          { "$ref": "#/components/parameters/cursor" }
        ],
        "responses": {
          "200": {
            "description": "A page of departures",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Availability" }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/workspaces/{workspaceId}/customers": {
      "parameters": [{ "$ref": "#/components/parameters/workspaceId" }],
      "get": {
        "tags": ["Customers"],
        "operationId": "listCustomers",
        "summary": "List customers",
        "parameters": [
          { "name": "q", "in": "query", "schema": { "type": "string" } },
          { "$ref": "#/components/parameters/limit" },
          { "$ref": "#/components/parameters/cursor" }
        ],
        "responses": {
          "200": {
            "description": "A page of customers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Customer" }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Customers"],
        "operationId": "createCustomer",
        "summary": "Create a customer",
        "parameters": [{ "$ref": "#/components/parameters/idempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["name", "email"],
                "properties": {
                  "name": { "type": "string" },
                  "email": { "type": "string", "format": "email" },
                  "phone": { "type": "string" },
                  "tags": { "type": "array", "items": { "type": "string" } },
                  "marketingConsent": { "type": "boolean" }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Customer created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "data": { "$ref": "#/components/schemas/Customer" } }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ValidationError" }
        }
      }
    },
    "/public/{workspaceSlug}/tours": {
      "security": [],
      "parameters": [{ "$ref": "#/components/parameters/workspaceSlug" }],
      "get": {
        "tags": ["Public"],
        "operationId": "listPublicTours",
        "summary": "List publicly bookable tours",
        "responses": {
          "200": {
            "description": "Published tours",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Tour" } }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/public/{workspaceSlug}/tours/{tourSlug}/availability": {
      "security": [],
      "parameters": [
        { "$ref": "#/components/parameters/workspaceSlug" },
        { "name": "tourSlug", "in": "path", "required": true, "schema": { "type": "string" } },
        { "$ref": "#/components/parameters/from" },
        { "$ref": "#/components/parameters/to" }
      ],
      "get": {
        "tags": ["Public"],
        "operationId": "listPublicAvailability",
        "summary": "List bookable departures for a tour",
        "responses": {
          "200": {
            "description": "Bookable departures",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Availability" }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/public/{workspaceSlug}/bookings": {
      "security": [],
      "parameters": [{ "$ref": "#/components/parameters/workspaceSlug" }],
      "post": {
        "tags": ["Public"],
        "operationId": "createPublicBooking",
        "summary": "Create a booking from a public booking flow",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": { "schema": { "$ref": "#/components/schemas/BookingCreate" } }
          }
        },
        "responses": {
          "201": {
            "description": "Booking created with a checkout URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        { "$ref": "#/components/schemas/Booking" },
                        {
                          "type": "object",
                          "properties": { "checkoutUrl": { "type": "string", "format": "uri" } }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "409": { "$ref": "#/components/responses/Conflict" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/public/bookings/{publicToken}": {
      "security": [],
      "parameters": [
        { "name": "publicToken", "in": "path", "required": true, "schema": { "type": "string" } }
      ],
      "get": {
        "tags": ["Public"],
        "operationId": "getPublicBooking",
        "summary": "Retrieve a booking by its guest token",
        "responses": {
          "200": { "$ref": "#/components/responses/BookingEnvelope" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API token issued in Settings → API keys. Inherits the role it was created under."
      }
    },
    "parameters": {
      "workspaceId": {
        "name": "workspaceId",
        "in": "path",
        "required": true,
        "schema": { "type": "string" },
        "example": "ws_01H8XZQ7"
      },
      "workspaceSlug": {
        "name": "workspaceSlug",
        "in": "path",
        "required": true,
        "schema": { "type": "string" },
        "example": "coastal-tours"
      },
      "bookingId": {
        "name": "bookingId",
        "in": "path",
        "required": true,
        "schema": { "type": "string" }
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 }
      },
      "cursor": {
        "name": "cursor",
        "in": "query",
        "description": "Opaque cursor from a previous response.",
        "schema": { "type": "string" }
      },
      "from": {
        "name": "from",
        "in": "query",
        "schema": { "type": "string", "format": "date" }
      },
      "to": { "name": "to", "in": "query", "schema": { "type": "string", "format": "date" } },
      "idempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Replaying a request with the same key returns the original result. Retained 24 hours.",
        "schema": { "type": "string", "format": "uuid" }
      }
    },
    "responses": {
      "BookingEnvelope": {
        "description": "A single booking",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": { "data": { "$ref": "#/components/schemas/Booking" } }
            }
          }
        }
      },
      "ValidationError": {
        "description": "Malformed or invalid input",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      },
      "Unauthenticated": {
        "description": "Missing or invalid credentials",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      },
      "Forbidden": {
        "description": "Authenticated but not permitted, or wrong workspace",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      },
      "NotFound": {
        "description": "No such record in this workspace",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      },
      "Conflict": {
        "description": "Capacity exhausted or concurrent modification",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      },
      "Unprocessable": {
        "description": "Valid shape, invalid business state",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "headers": {
          "Retry-After": { "schema": { "type": "integer" } },
          "X-RateLimit-Limit": { "schema": { "type": "integer" } },
          "X-RateLimit-Remaining": { "schema": { "type": "integer" } },
          "X-RateLimit-Reset": { "schema": { "type": "integer" } }
        },
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      }
    },
    "schemas": {
      "BookingStatus": {
        "type": "string",
        "enum": ["PENDING", "CONFIRMED", "CANCELLED", "COMPLETED", "NO_SHOW"]
      },
      "PaymentStatus": {
        "type": "string",
        "enum": ["UNPAID", "PENDING", "PAID", "PARTIALLY_REFUNDED", "REFUNDED"]
      },
      "Pagination": {
        "type": "object",
        "required": ["hasMore"],
        "properties": {
          "nextCursor": { "type": ["string", "null"] },
          "hasMore": { "type": "boolean" }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "string", "example": "VALIDATION_ERROR" },
              "message": { "type": "string" },
              "details": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "field": { "type": "string" },
                    "issue": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      },
      "ParticipantInput": {
        "type": "object",
        "required": ["name"],
        "properties": {
          "name": { "type": "string" },
          "ageBand": { "type": "string", "enum": ["ADULT", "CHILD", "INFANT", "SENIOR"] },
          "dietaryNotes": { "type": "string" },
          "accessibilityNotes": { "type": "string" },
          "emergencyContact": { "type": "string" }
        }
      },
      "Participant": {
        "allOf": [
          { "$ref": "#/components/schemas/ParticipantInput" },
          {
            "type": "object",
            "properties": {
              "id": { "type": "string" },
              "waiverSigned": { "type": "boolean" },
              "checkedInAt": { "type": ["string", "null"], "format": "date-time" }
            }
          }
        ]
      },
      "BookingCreate": {
        "type": "object",
        "required": ["availabilityId", "customer", "participants"],
        "properties": {
          "availabilityId": { "type": "string" },
          "customer": {
            "type": "object",
            "required": ["name", "email"],
            "properties": {
              "name": { "type": "string" },
              "email": { "type": "string", "format": "email" },
              "phone": { "type": "string" }
            }
          },
          "participants": {
            "type": "array",
            "minItems": 1,
            "items": { "$ref": "#/components/schemas/ParticipantInput" }
          },
          "addOns": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["id", "quantity"],
              "properties": {
                "id": { "type": "string" },
                "quantity": { "type": "integer", "minimum": 1 }
              }
            }
          },
          "paymentMode": {
            "type": "string",
            "enum": ["CHECKOUT", "PAYMENT_LINK", "DEPOSIT", "MANUAL"],
            "default": "CHECKOUT"
          },
          "notes": { "type": "string" }
        }
      },
      "Booking": {
        "type": "object",
        "required": ["id", "status", "paymentStatus", "totalAmount", "currency"],
        "properties": {
          "id": { "type": "string", "example": "bkg_01H8XZQ7" },
          "status": { "$ref": "#/components/schemas/BookingStatus" },
          "paymentStatus": { "$ref": "#/components/schemas/PaymentStatus" },
          "totalAmount": {
            "type": "integer",
            "description": "Amount in the currency's minor unit.",
            "example": 24000
          },
          "currency": { "type": "string", "example": "USD" },
          "availabilityId": { "type": "string" },
          "customerId": { "type": "string" },
          "participantCount": { "type": "integer" },
          "participants": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Participant" }
          },
          "paymentIntentId": { "type": ["string", "null"] },
          "publicToken": { "type": ["string", "null"] },
          "notes": { "type": ["string", "null"] },
          "expiresAt": { "type": ["string", "null"], "format": "date-time" },
          "confirmedAt": { "type": ["string", "null"], "format": "date-time" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "Tour": {
        "type": "object",
        "required": ["id", "name"],
        "properties": {
          "id": { "type": "string", "example": "tour_01H8XZQ7" },
          "slug": { "type": "string" },
          "name": { "type": "string" },
          "description": { "type": "string" },
          "durationMinutes": { "type": "integer" },
          "basePrice": { "type": "integer" },
          "currency": { "type": "string" },
          "capacity": { "type": "integer" },
          "meetingPoint": { "type": ["string", "null"] },
          "isPublic": { "type": "boolean" },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "Availability": {
        "type": "object",
        "required": ["id", "tourId", "startsAt", "capacity", "seatsRemaining"],
        "properties": {
          "id": { "type": "string", "example": "avl_01H8XZQ7" },
          "tourId": { "type": "string" },
          "startsAt": { "type": "string", "format": "date-time" },
          "capacity": { "type": "integer" },
          "seatsRemaining": { "type": "integer" },
          "bookedSeats": { "type": "integer" },
          "status": {
            "type": "string",
            "enum": ["SCHEDULED", "BOARDING", "DEPARTED", "DELAYED", "COMPLETED", "CANCELLED"]
          },
          "guideId": { "type": ["string", "null"] },
          "driverId": { "type": ["string", "null"] },
          "vehicleId": { "type": ["string", "null"] },
          "bookingCutoffAt": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "Customer": {
        "type": "object",
        "required": ["id", "name", "email"],
        "properties": {
          "id": { "type": "string", "example": "cus_01H8XZQ7" },
          "name": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "phone": { "type": ["string", "null"] },
          "companyId": { "type": ["string", "null"] },
          "tags": { "type": "array", "items": { "type": "string" } },
          "marketingConsent": { "type": "boolean" },
          "totalBookings": { "type": "integer" },
          "lifetimeValue": { "type": "integer" },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      }
    }
  }
}
