Champagne

Overview

Explore the public GraphQL endpoint, available queries, and generated contract.

The GraphQL endpoint is /api/graphql. It accepts queries over GET or POST and mutations over POST. Stripe delivers signed invoice/payment/credit-note webhooks separately to /api/webhooks/stripe. Invoice queries read only PostgreSQL; automatic resumable import and webhook jobs maintain the projection. The invoice list exposes synchronization progress separately from local totals. GraphiQL is available at that endpoint in development, not production.

Floor-plan image imports use startFloorPlanImport, floorPlanImport and cancelFloorPlanImport for user/club/category-scoped draft jobs. They require catalog_manage and expose deterministic native geometry, original-source crop/registration, regional reference/draft inspection and bounded name-based templates. Booking bindings are explicit and independent of visuals; labels never create resources. Early/periodic/final inspections and honest review notes precede human acceptance, without claiming a PNG proves fidelity. Saving remains explicit through the existing floor-plan mutation; see Floor plan imports.

Floor-plan elements support optional chairPositions, normalized perimeter fractions for individually positioned attached chairs. Null retains automatic placement; saves validate counts and reject object/chair collisions or insufficient safe space. See Reservations for the input/output contract.

Artwork generation and brush editing support per-request tracing options, an allowlisted Sunburst/Flare generation model (Sunburst by default), and a separate Gemini brush model. Brush classification defaults to DeepSeek + Gemini; the request-local classifyBrush: false option skips DeepSeek and lets Gemini infer and edit together, with the same required rendered visual review. The scoped floorPlanArtworkProgress query provides lightweight live milestones without image or prompt payloads; see Reservations for defaults, authorization, polling and failure semantics.

Available queries

QueryPurpose
myNotificationsPaginate the calling member's inbox and unread count within a club.
myNotificationPreferencesRead the calling member's per-category delivery preferences.
memberViewerReturn the calling member's person, club and guest expiry, or null.
guestInvitationsList a club's guest invitations, or the calling member's own.
guestInvitationPreview a guest invitation link before accepting it.
staffViewerReturn the calling staff user's clubs, or null.
peoplePageSelect a filtered, paginated list of people and its total count.
personFetch one person's details by ID, optionally scoped to a club.
clubDirectoryFind clubs for the people selection's club filter.
clubWebhookEndpointsList the selected club's outbound webhook endpoints (admin).
clubWebhookEventCatalogList implemented notification types and descriptions (admin).
peopleFetch up to 100 demo people without filtering or pagination.
plansFetch active plans globally or through a club's selected merchant.
reservationCategoriesList a club's persisted reservation categories.
reservationAvailabilityList session occurrences and blocked periods for a type and UTC range.
reservationsPaginate reservations filtered by club and type.
reservationFetch one staff-visible reservation.
myReservationFetch one reservation owned by the authenticated member.
reservationChatRead a staff-visible reservation conversation.
reservationChatsList the staff account's active or archived chats and unread state.
myReservationChatRead the authenticated member's own reservation conversation.
hostedClubResolve the public club identity associated with a hosted slug.
invoicesFilter and paginate eligible sandbox subscription invoices.
invoiceRetrieve an invoice, its payments, taxes, lines and credit notes.
onboardingFetch one club onboarding by ID.
onboardingsList the onboardings configured for a club.
applicationsList a club's applications.
applicationRetrieve an application snapshot for back-office review.
applicationHistoryRead a club application’s recorded status transitions and attribution.
membershipOffersLoad club-scoped sandbox offers with explicit readiness and reason.
merchantProfilesList same-space merchants and the club's selected local profile ID.
membershipCheckoutStatusConfirm the returned session's subscription for its person and club.
addonOffersLoad club-scoped one-time add-on offers with readiness and reason.
addonCheckoutStatusConfirm the returned session's one-time add-on payment.

The people selection reference documents arguments, return fields, validation, and selection behavior, including the updatePerson mutation for personal details, updatePersonNote for saving or clearing a person's note, updatePersonStatus for suspension, bans, disablement, and reactivation, and createPerson for staff-created people. The authentication reference documents member and staff sign in, password choice after onboarding, person creation, and account activation with activateMemberAccount and resendPersonActivation. The guests reference documents inviteGuest and acceptGuestInvitation. The plans reference documents club-scoped listing and Stripe-synchronized create, update and delete mutations. Onboarding mutations are also available. Mutations use POST. Person management requires staff authentication and authorized club access.

People have a PersonType (individual or entreprise) and a PersonStatus (active, suspended, no_booking, banned, or disabled); enterprise profiles can also include optional SIRET and TVA identifiers. The billing reference documents createMembershipCheckout(personId: ID!, clubId: ID!, planId: ID!, requestId: ID!) and createAddonCheckout(personId: ID!, clubId: ID!, planId: ID!, requestId: ID!). The merchant reference documents selection, creation, activation, verification and catalogue preparation. The invoice reference documents refreshInvoice, creditInvoice and invoice queries, all requiring club context. These operations enforce authenticated club access. The webhook endpoints reference covers admin-only management of a club's endpoints: adding, pausing and deleting them, their signing secrets and rotation. The reservation reference documents member-only createReservation(input: CreateReservationInput!), which creates a pending request using the signed-in member's identity, plus connected-account Checkout for paid bookings. Members can list their club's reservation categories; type management, direct staff booking, reservation editing, and professional messages remain staff-only. Admins also manage weekly/custom sessions and blocked periods. Members can edit pending requests, cancel their own requests, accept staff-proposed times, and access only conversations they own.

Run a query

Click the pencil on the example to edit it in Monaco, then select Run query. Results come from the live API. Expand Variables to supply a JSON object.

query People {
  people {
    id
    firstName
    lastName
    email
  }
}

The query returns up to 100 records from public.person, ordered by first name, last name, and ID. Compose seeds fictional examples, including:

{
  "id": "00000000-0000-4000-8000-000000000001",
  "firstName": "Alex",
  "lastName": "Morgan",
  "email": "alex@example.com"
}

The GraphQL response wraps the records in {"data":{"people":[...]}}.

Set APP_URL to the URL printed by make dev or make worktree-up:

curl "$APP_URL/api/graphql" \
  -H 'Content-Type: application/json' \
  --data '{"query":"query People { people { id firstName lastName email } }"}'

Extend the contract

The schema is split into one module per domain (reservations alone span catalog, sessions, waitlists, attendance, chats, staff availability, external booking and bookings). Each module declares its own root fields with extend type Query and extend type Mutation; roots.ts keeps shared enums and the bug report. backend/src/graphql/schema/index.ts composes them for Yoga; code generation reads the same module files. The public contract is unchanged by this organization.

  1. Edit the relevant module in backend/src/graphql/schema/ and web/src/graphql/operations.graphql.
  2. Run make codegen to generate resolver and operation types.
  3. Update the resolver and native Drizzle query.
  4. Update the frontend and tests, then run make check and make test.

Do not edit generated files by hand. CI checks for code generation drift.

Web GraphQL debugger

The Developer button at the bottom of the back-office navigation opens a popover with Doc and GraphQL debugger actions. The debugger opens in a fixed, non-modal bottom panel by default. The rest of the app stays clickable, so requests appear live while you navigate. Minimize collapses it to a small GraphQL launcher; click it to reopen. Recording continues while minimized. Expand switches to the fullscreen view; Restore returns to the panel without resetting the selected request or recording state. Each new opening starts in the bottom panel. It records actual web requests, not cache reads, and displays syntax-highlighted queries, variables, and formatted responses with HTTP status, duration, and failure or cancellation state. It does not execute or replay operations.

Recording starts automatically in development, and when the debugger is opened in a production build. There are no pause or clear controls; reload to reset history. Only the latest 50 requests are retained in memory. Variables and response displays are capped at 64,000 characters. Values are not redacted: passwords, tokens, and personal data may be visible. Review debugger output before sharing it. Headers are never recorded and nothing is persisted or sent to a telemetry service.

Person information is restricted to its owner and authorized club administrators. Applicant access uses a scoped token or authenticated session. See the authentication reference for public operations, session revocation and password recovery.

See Onboarding operations for ordered question and special blocks, signature document fields, and save validation.

Application review follows explicit status transitions. The club-scoped applicationHistory query returns recorded transitions with server dates, origins and historical rejection reasons.

Club credits

See Club credits for credit packs, balances, refill history, credit-priced add-ons and reservations, and cancellation refunds. Membership subscriptions stay billed in money.

Operation limits

Every GraphQL document is checked before resolver execution, including public operations. The server accepts at most 65,536 query characters and 10,000 parser tokens. After expanding fragment spreads, limits are 20 root fields, 50 aliases, 1,000 fields, 15 field levels and 2,000 selection visits. Fragment cycles cannot bypass these limits. All operations in a document contribute to the budget, even when only one operation is selected; conditional directives do not reduce it.

A static cost budget of 100,000 also applies: each field costs its inherited weight, and each list layer multiplies descendant weight by 10. This is a conservative structural estimate, not a measurement of SQL work or returned rows. Existing pagination and business limits still apply. Excessive documents receive OPERATION_LIMIT_EXCEEDED without invoking resolvers; parser token limits produce a GraphQL syntax error. HTTP batching remains disabled.

Error codes

Expected errors are returned unmasked with errors[].extensions.code:

CodeMeaning
BAD_USER_INPUTRejected input or a business refusal; the message can be shown as is. Malformed identifiers return Invalid ID.
UNAUTHENTICATEDThe caller is not signed in, not as the member or staff account the field requires, or a signed-in staff account lacks the permission or club access the operation needs. Anonymous callers read "You do not have access to this information. Please sign in."; signed-in staff read "Your access does not include this action."
FORBIDDENOnly for staff professional documents a signed-in staff account may not open, and for the floor plan artwork debugger.
NOT_FOUNDThe requested club setting does not exist.
CHECKOUT_EXPIRED, INSUFFICIENT_CREDITSPayment outcomes the apps handle explicitly.
SERVICE_UNAVAILABLEA service this deployment does not configure (for example sandbox billing, webhooks or floor plans), or a provider that could not be reached. The message names the service.

PROPOSAL_CHANGED and PROPOSAL_UNAVAILABLE are not extensions.code values: they are ReservationResult.errorCode values returned in data by the reservation proposal mutations (see reservations).

Invalid pages for paginated history fields return BAD_USER_INPUT with Choose a valid page.. Any other failure is masked as described below.

Request diagnostics

GraphQL responses include a server-generated X-Request-Id header. Unexpected errors keep the generic Unexpected error. message and include the same ID in errors[].extensions.requestId. Supply this ID when reporting an incident; an HTTP 200 response can still contain a GraphQL error. Incoming request IDs are not trusted or reused.

The backend emits a correlated Effect error log for unexpected failures, including failures during authentication-context construction. Logs identify the operation kind and, where available, the schema's root field. Client-supplied operation names, aliases, variables, headers and request bodies are never copied into these logs. Expected validation, authentication and business errors do not produce unexpected-error logs.

Console cause diagnostics contain recognized error categories, allowlisted technical codes, stack fingerprints and bounded redacted messages/frames. Exported traces add original exception messages, stacks and error-owned properties, including nested provider causes. These sensitive details are never returned in API responses. Neither path can recover causes already discarded by a provider adapter.

Trace Correlation

When an Effect OTLP endpoint is configured, unexpected masked errors also include errors[].extensions.traceId and spanId: the actual GraphQL server span's identifiers, shared with its sanitized server diagnostic and nested Effect service spans. These are correlation metadata, never raw messages or stacks. IDs are absent when request tracing is not configured. Production/staging Terraform can enable authenticated Cockpit tracing; local development uses Waggle. Client trace headers are not trusted or used as parents.

Error reports in both frontends prefer the configured local Waggle /traces/<traceId>?span=<spanId> link for valid trace context, otherwise retaining request-ID log search. Production reports retain the access-controlled Cockpit logs dashboard rather than inventing a trace link.

Background floor-plan imports run in a separate floorPlan.import trace, linked to the initiating GraphQL span with a follows-from span link. Its attributes include the import ID and initiating request/trace/span IDs. The job continues after the start response ends; its failure does not retroactively fail that response. Console import reports remain sanitized, while the failed trace retains the original error cause. Client artwork-debugger traceId values remain separate application identifiers, not OpenTelemetry trace IDs.

Sur cette page