# Guests

Invite guests by email and give them a temporary member account for 12, 24 or 48 hours.

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

Club administrators and members can invite a guest by email. When the guest accepts,
Champagne creates a temporary member account in the inviter's club that stays active for
the duration the inviter chose: 12, 24 or 48 hours.

1. The host calls `inviteGuest(clubId, email, durationHours)`.
2. Champagne emails the guest a `/guest-invitation?token=…` link on the member site.
3. The guest previews it with `guestInvitation(token)` and accepts with `acceptGuestInvitation`.
4. Acceptance creates a `guest_access` grant and signs the guest in until that grant expires.

## Invite a guest [#invite-a-guest]

```graphql
mutation InviteGuest($clubId: ID!, $email: String!, $durationHours: Int!) {
  inviteGuest(clubId: $clubId, email: $email, durationHours: $durationHours)
}
```

```json
{
  "clubId": "51000000-0000-4000-8000-000000000051",
  "email": "guest@example.com",
  "durationHours": 24
}
```

| Argument        | Type      | Description                                                     |
| --------------- | --------- | --------------------------------------------------------------- |
| `clubId`        | `ID!`     | Club the guest joins. Must be the inviter's club.               |
| `email`         | `String!` | Guest email. Trimmed and compared case-insensitively.           |
| `durationHours` | `Int!`    | Guest access granted on acceptance: exactly `12`, `24` or `48`. |

Callers must be either a staff account with `members_manage` in `clubId` (club
administrators hold every permission; see [staff permissions](/fr/graphql/auth#individual-staff-permissions)) or a signed-in **member** of that club
whose effective status is `active`. Staff without that permission, guests, anonymous
callers and other clubs receive `UNAUTHENTICATED`. Other durations, invalid emails
and emails that already belong to a regular person of the club fail with
`BAD_USER_INPUT`. An email belonging to a current or former guest can be invited again.

The mutation returns `true` only after the email was delivered. The email contains a
single-use link to `CONSUMER_ORIGIN/guest-invitation?token=…`, valid for 7 days. The
guest access duration only starts when the guest accepts. Inviting the same email again
from the same inviter replaces their unaccepted invitation. If delivery fails, the new
invitation is removed and a retryable error is returned. No token is returned to the caller.

## List invitations [#list-invitations]

```graphql
query GuestInvitations($clubId: ID!) {
  guestInvitations(clubId: $clubId) {
    id
    email
    durationHours
    status
    invitedBy
    guestName
    createdAt
    expiresAt
    accessExpiresAt
  }
}
```

Administrators see every invitation of the club; members see only the invitations they
sent. Results are ordered newest first and limited to 100.

| Field             | Type                     | Description                                                          |
| ----------------- | ------------------------ | -------------------------------------------------------------------- |
| `status`          | `GuestInvitationStatus!` | `pending`, `expired` (link unused and elapsed), `active` or `ended`. |
| `invitedBy`       | `String!`                | Member full name or staff name of the inviter.                       |
| `guestName`       | `String`                 | Guest full name once accepted.                                       |
| `expiresAt`       | `String!`                | ISO timestamp after which the link can no longer be accepted.        |
| `accessExpiresAt` | `String`                 | ISO end of the temporary account granted by this invitation.         |

An accepted invitation is `active` while its access grant is neither expired nor revoked,
and `ended` afterwards.

## Accept an invitation [#accept-an-invitation]

The public `guestInvitation` query previews a link before acceptance. It returns `null`
for unknown tokens; `expired` is `true` once the link was used or has elapsed.

```graphql
query GuestInvitationPreview($token: String!) {
  guestInvitation(token: $token) {
    email
    clubName
    invitedBy
    durationHours
    expired
  }
}
mutation AcceptGuestInvitation(
  $token: String!
  $firstName: String!
  $lastName: String!
  $password: String!
) {
  acceptGuestInvitation(
    token: $token
    firstName: $firstName
    lastName: $lastName
    password: $password
  ) {
    token
    personId
    clubId
  }
}
```

Names need 1–100 characters and the password follows the member password policy. Acceptance
atomically creates the guest person and login (or renews a former guest with the same email),
creates a `guest_access` row ending at now plus `durationHours`, consumes the link, revokes the
guest's older sessions and returns a member session. Used, expired or unknown links fail with
`BAD_USER_INPUT`, as does an email that became a regular member of the club in the meantime.

## Guest accounts [#guest-accounts]

Guests are ordinary people whose latest `guest_access` grant determines their temporary access.
The derived `guestExpiresAt` field remains exposed on `Person`, `PersonDetail` and `memberViewer`;
`PersonDetail.guestAccess` also exposes the latest grant and its structured inviter. Until expiry,
a guest can sign in with
`loginMember`, read their own profile and see the reservations they are invited to. Guests do
not book themselves: the member site offers them no club schedule. They cannot start
membership, add-on or credit Checkout and cannot invite other guests. The member site shows
them only Overview, Reservations (the reservations they were invited to) and
Notifications. Once `guestExpiresAt` has
passed, existing sessions stop resolving and sign-in fails with
`Your guest access has ended.` The person and access records are kept for history. If an inviter
account is deleted, the access record retains its inviter kind while the inviter ID and name become
null; clients should display a deleted-user label.

## Reservation invitations requiring accounts [#reservation-invitations-requiring-accounts]

Clubs can require accounts for reservation guests in admin Settings. See
[Reservation guest accounts](/fr/graphql/reservations#require-guest-accounts) for the policy queries
and invitation arguments. These invitations appear in `guestInvitations` and use the same
`guestInvitation` preview and `acceptGuestInvitation` registration contract. Acceptance also links
the person to the reservation and marks their invitation accepted in the same transaction.
A reservation invitation can complete an existing person record that has no login credentials;
it never overwrites an existing member password. Invalidated reservation links cannot register.
Sending a regular Guests-tab invitation does not invalidate invitations tied to reservations.
