# Overview

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

Source: https://docs.champagne.dev-ukab.fr/fr/graphql

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](/fr/graphql/reservations#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](/fr/graphql/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](/fr/graphql/reservations#live-artwork-progress) for defaults, authorization,
polling and failure semantics.

[Bug reports](https://docs.champagne.dev-ukab.fr/fr/graphql/bug-reports)

Submit staff bug reports to the Champagne GitHub repository.

[Authentication](https://docs.champagne.dev-ukab.fr/fr/graphql/auth)

Sign members in per club, choose a password after onboarding, create people, and sign staff in
across clubs.

[Guests](https://docs.champagne.dev-ukab.fr/fr/graphql/guests)

Invite guests by email for a temporary 12, 24 or 48 hour member account.

[Building entries](https://docs.champagne.dev-ukab.fr/fr/graphql/building-entries)

Record member entries into the building and review each person's entry history.

[People selection](https://docs.champagne.dev-ukab.fr/fr/graphql/people)

Select individuals or enterprises with type, search, club, membership, age, and gender filters,
fixed sorting, pagination, and details. Edit legal status with enterprise SIRET and TVA.

[Member attributes](https://docs.champagne.dev-ukab.fr/fr/graphql/person-attributes)

Define typed club attributes, save onboarding answers to them, and edit or filter them on
people.

[Sandbox membership billing](https://docs.champagne.dev-ukab.fr/fr/graphql/billing)

Compare three annual IÉNA offers and start Stripe-hosted subscription Checkout.

[Merchant profiles](https://docs.champagne.dev-ukab.fr/fr/graphql/merchants)

Select, create, activate and prepare a connected sandbox merchant for a club.

[Plans](https://docs.champagne.dev-ukab.fr/fr/graphql/plans)

List subscription and add-on plans with amounts, currencies, and intervals.

[Reservations](https://docs.champagne.dev-ukab.fr/fr/graphql/reservations)

Request reservations as a member; manage types and browse reservations as staff.

[Notifications](https://docs.champagne.dev-ukab.fr/fr/graphql/notifications)

Read member notifications and configure in-app and email preferences per category.

[Invoices](https://docs.champagne.dev-ukab.fr/fr/graphql/invoices)

Browse sandbox invoices, payments and PDFs, and issue full credit notes.

[Applications](https://docs.champagne.dev-ukab.fr/fr/graphql/applications)

Start, advance, inspect, and review immutable onboarding submissions.

[Onboarding](https://docs.champagne.dev-ukab.fr/fr/graphql/onboarding)

List, create, read, and edit a club's audience-specific onboardings.



## Available queries [#available-queries]

| Query                       | Purpose                                                                |
| --------------------------- | ---------------------------------------------------------------------- |
| `myNotifications`           | Paginate the calling member's inbox and unread count within a club.    |
| `myNotificationPreferences` | Read the calling member's per-category delivery preferences.           |
| `memberViewer`              | Return the calling member's person, club and guest expiry, or null.    |
| `guestInvitations`          | List a club's guest invitations, or the calling member's own.          |
| `guestInvitation`           | Preview a guest invitation link before accepting it.                   |
| `staffViewer`               | Return the calling staff user's clubs, or null.                        |
| `peoplePage`                | Select a filtered, paginated list of people and its total count.       |
| `person`                    | Fetch one person's details by ID, optionally scoped to a club.         |
| `clubDirectory`             | Find clubs for the people selection's club filter.                     |
| `clubWebhookEndpoints`      | List the selected club's outbound webhook endpoints (admin).           |
| `clubWebhookEventCatalog`   | List implemented notification types and descriptions (admin).          |
| `people`                    | Fetch up to 100 demo people without filtering or pagination.           |
| `plans`                     | Fetch active plans globally or through a club's selected merchant.     |
| `reservationCategories`     | List a club's persisted reservation categories.                        |
| `reservationAvailability`   | List session occurrences and blocked periods for a type and UTC range. |
| `reservations`              | Paginate reservations filtered by club and type.                       |
| `reservation`               | Fetch one staff-visible reservation.                                   |
| `myReservation`             | Fetch one reservation owned by the authenticated member.               |
| `reservationChat`           | Read a staff-visible reservation conversation.                         |
| `reservationChats`          | List the staff account's active or archived chats and unread state.    |
| `myReservationChat`         | Read the authenticated member's own reservation conversation.          |
| `hostedClub`                | Resolve the public club identity associated with a hosted slug.        |
| `invoices`                  | Filter and paginate eligible sandbox subscription invoices.            |
| `invoice`                   | Retrieve an invoice, its payments, taxes, lines and credit notes.      |
| `onboarding`                | Fetch one club onboarding by ID.                                       |
| `onboardings`               | List the onboardings configured for a club.                            |
| `applications`              | List a club's applications.                                            |
| `application`               | Retrieve an application snapshot for back-office review.               |
| `applicationHistory`        | Read a club application’s recorded status transitions and attribution. |
| `membershipOffers`          | Load club-scoped sandbox offers with explicit readiness and reason.    |
| `merchantProfiles`          | List same-space merchants and the club's selected local profile ID.    |
| `membershipCheckoutStatus`  | Confirm the returned session's subscription for its person and club.   |
| `addonOffers`               | Load club-scoped one-time add-on offers with readiness and reason.     |
| `addonCheckoutStatus`       | Confirm the returned session's one-time add-on payment.                |

The [people selection reference](/fr/graphql/people) 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](/fr/graphql/auth) documents member and staff sign in,
password choice after onboarding, person creation, and account activation with
`activateMemberAccount` and `resendPersonActivation`. The
[guests reference](/fr/graphql/guests) documents `inviteGuest` and `acceptGuestInvitation`. The
[plans reference](/fr/graphql/plans) 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](/fr/graphql/billing) documents
`createMembershipCheckout(personId: ID!, clubId: ID!, planId: ID!, requestId: ID!)` and
`createAddonCheckout(personId: ID!, clubId: ID!, planId: ID!, requestId: ID!)`.
The [merchant reference](/fr/graphql/merchants) documents selection, creation, activation,
verification and catalogue preparation. The [invoice reference](/fr/graphql/invoices)
documents `refreshInvoice`, `creditInvoice` and invoice queries, all requiring club context.
These operations enforce authenticated club access.
The [webhook endpoints reference](/fr/graphql/webhooks) covers admin-only management of a club's
endpoints: adding, pausing and deleting them, their signing secrets and rotation.
The [reservation reference](/fr/graphql/reservations) 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 [#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.

```graphql
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:

```json
{
  "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`:

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

## Extend the contract [#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 [#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](/fr/graphql/onboarding) for ordered question and special blocks, signature document fields, and save validation.

Application review follows explicit status transitions. The club-scoped
[`applicationHistory` query](/fr/graphql/applications#status-transitions-and-history) returns
recorded transitions with server dates, origins and historical rejection reasons.

## Club credits [#club-credits]

See [Club credits](/fr/graphql/credits) for credit packs, balances, refill history,
credit-priced add-ons and reservations, and cancellation refunds. Membership
subscriptions stay billed in money.

## Operation limits [#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 [#error-codes]

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

| Code                                       | Meaning                                                                                                                                                                                                                                                                                                                          |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BAD_USER_INPUT`                           | Rejected input or a business refusal; the message can be shown as is. Malformed identifiers return `Invalid ID.`                                                                                                                                                                                                                 |
| `UNAUTHENTICATED`                          | The 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." |
| `FORBIDDEN`                                | Only for staff professional documents a signed-in staff account may not open, and for the floor plan artwork debugger.                                                                                                                                                                                                           |
| `NOT_FOUND`                                | The requested club setting does not exist.                                                                                                                                                                                                                                                                                       |
| `CHECKOUT_EXPIRED`, `INSUFFICIENT_CREDITS` | Payment outcomes the apps handle explicitly.                                                                                                                                                                                                                                                                                     |
| `SERVICE_UNAVAILABLE`                      | A 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](/fr/graphql/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 [#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 [#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.
