GraphQL API

Reservory's GraphQL API is a flexible alternative to REST for discovery and the booking flow. It supports queries, mutations, fragments, aliases, @include/@skip directives, and introspection.

Endpoint

POST https://api.reservory.com/api/graphql
GET  https://api.reservory.com/api/graphql   # returns { sdl } for tooling

Current Limitations

  • Public surface only: discovery + booking flow. Operator-scoped queries (bookings, customers, refunds) are REST-only for now.
  • Input objects via variables: pass input objects (e.g. CreateBookingInput) through a $variable, not as inline object literals.
  • No subscriptions: use webhooks for real-time events.
  • Rate limit: 60 requests/minute per IP.

Example Queries

Get Experience Metadata

query GetExperience($tenant: String!, $experience: String!) {
  experience(tenant: $tenant, experience: $experience) {
    id
    name
    slug
    currency
    basePriceCents
    minPartySize
    maxPartySize
    durationMinutes
    requiresWaiver
  }
}

# Variables:
# { "tenant": "acme-escape", "experience": "the-vault" }

List Available Slots

query AvailableSlots($expId: ID!, $from: String, $to: String) {
  availableSlots(experienceId: $expId, fromIso: $from, toIso: $to) {
    id
    startsAt
    endsAt
    capacity
    seatsRemaining
    priceCents
  }
}

# Variables:
# {
#   "expId": "00000000-0000-0000-0000-0000000000aa",
#   "from": "2026-05-25T00:00:00Z",
#   "to": "2026-06-01T23:59:59Z"
# }

Mutations

Create Booking

mutation CreateBooking($input: CreateBookingInput!) {
  createBooking(input: $input) {
    booking_id
    booking_token
  }
}

# Variables:
# {
#   "input": {
#     "hold_id": "...",
#     "slot_id": "...",
#     "experience_id": "...",
#     "venue_id": "...",
#     "customer_email": "alice@example.com",
#     "customer_first_name": "Alice",
#     "guest_count": 2
#   }
# }

Cancel Booking (by token)

mutation CancelBooking($token: String!) {
  cancelBookingByToken(token: $token) {
    ok
    reason
  }
}

# Variables:
# { "token": "<booking_token from createBooking>" }

Schema

The live SDL is served from GET /api/graphql as { sdl } (and via introspection):

type Query {
  experience(tenant: String!, experience: String!): Experience
  availableSlots(experienceId: ID!, fromIso: String, toIso: String): [Slot!]!
}

type Mutation {
  createBooking(input: CreateBookingInput!): BookingResponse!
  cancelBookingByToken(token: String!): CancelBookingResponse!
}

input CreateBookingInput {
  hold_id: ID!
  slot_id: ID!
  experience_id: ID!
  venue_id: ID!
  customer_email: String!
  customer_first_name: String!
  customer_last_name: String
  customer_phone: String
  guest_count: Int!
}

type Experience {
  id: ID!
  name: String!
  slug: String!
  currency: String!
  basePriceCents: Int!
  minPartySize: Int!
  maxPartySize: Int!
  durationMinutes: Int!
  requiresWaiver: Boolean!
}

type Slot {
  id: ID!
  startsAt: String!
  endsAt: String!
  capacity: Int!
  seatsRemaining: Int!
  priceCents: Int
}

type BookingResponse { booking_id: ID!  booking_token: String! }
type CancelBookingResponse { ok: Boolean!  reason: String }

JavaScript Example

const query = `
  query AvailableSlots($expId: ID!) {
    availableSlots(experienceId: $expId) {
      id
      startsAt
      seatsRemaining
    }
  }
`;

const response = await fetch('https://api.reservory.com/api/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query,
    variables: { expId: '00000000-0000-0000-0000-0000000000aa' },
  }),
});

const { data, errors } = await response.json();
if (errors) console.error(errors);
else console.log(data.availableSlots);

Future Roadmap

  • Operator-scoped queries (bookings, customers, refunds) with API-key auth
  • Subscriptions for real-time slot updates
  • Migration to a spec-compliant server (graphql-yoga)