Skip to content

DEVELOPER REFERENCE

The complete API contract.

Every query, mutation, argument, type and permission directive from this release’s GraphQL specification.

497 of 497 definitions

! means required; brackets denote a list. Built-in scalars are ID, String, Int, Float and Boolean. Expand a definition for its exact signature and directives. Follow a related type to clear filters and jump to it.

Schemaschema

Events platform — GraphQL contract v1 (T0). The source of truth for every track. Conventions - Contract-first: tracks implement resolvers against this file; changes go through the owning track's PR and the schema-snapshot gate (QA-GATE-09: Quinn + Lain approval label). - Fail-closed authorisation: every Query and Mutation field declares @auth. A principal holding none of the listed roles gets FORBIDDEN before any resolver runs. There is no OPERATOR role: AF operators have no path to data (SEC-07, SEC-55). - @pii marks contact-class personal data (email, phone, answers). A @pii field carries its own @auth, never lists ANON/HOST/INVESTOR, and is audited on read (SEC-08, SEC-26/27, SEC-38). - @personal marks names and free text about a person (display names, reasons). It carries its own @auth ("role-authorised"), or @personal(self: true) when only the subject may read it; it is read-audited like @pii (T3). Both rules are enforced when the schema boots. - @visibility(class, scope) classifies every field of every company/progress/investor-domain type; it must equal packages/domain/src/authz/registry.ts (QA-GATE-23). The resolver still asks canSee() per field instance (SEC-14): the directive is the declaration, not the check. - @idempotent mutations require the `Idempotency-Key` header (1–255 visible ASCII), scoped per principal; replay returns the original; same key + different payload → IDEMPOTENCY_KEY_REUSED (REQ-REG-02). - @stepUp requires a passkey/TOTP step-up within 5 minutes (REQ-ATT-01, REQ-ADM-13, SEC-09). - The investor role may only execute persisted operations (REQ-VC-13); introspection is off in production (SEC-29); depth and complexity are limited (apps/api/src/limits.ts). - Errors: GraphQL errors with `extensions.code` from ErrorCode. Private resources the caller may not see resolve to null (→ 404 in the web app), never FORBIDDEN, so existence does not leak (SEC-14, REQ-ADM-06). - Time: DateTime is a UTC instant (ISO 8601, Z). LocalDateTime is zone-less wall-clock in the accompanying TimeZone. Durations are integers in minutes, never milliseconds. - Nothing derived, scored, ranked or trended is exposed to founders or investors (REQ-PRG-07).

"""
Events platform — GraphQL contract v1 (T0). The source of truth for every track.

Conventions
- Contract-first: tracks implement resolvers against this file; changes go through the owning
  track's PR and the schema-snapshot gate (QA-GATE-09: Quinn + Lain approval label).
- Fail-closed authorisation: every Query and Mutation field declares @auth. A principal holding
  none of the listed roles gets FORBIDDEN before any resolver runs. There is no OPERATOR role:
  AF operators have no path to data (SEC-07, SEC-55).
- @pii marks contact-class personal data (email, phone, answers). A @pii field carries its own
  @auth, never lists ANON/HOST/INVESTOR, and is audited on read (SEC-08, SEC-26/27, SEC-38).
- @personal marks names and free text about a person (display names, reasons). It carries its
  own @auth ("role-authorised"), or @personal(self: true) when only the subject may read it; it
  is read-audited like @pii (T3). Both rules are enforced when the schema boots.
- @visibility(class, scope) classifies every field of every company/progress/investor-domain
  type; it must equal packages/domain/src/authz/registry.ts (QA-GATE-23). The resolver still
  asks canSee() per field instance (SEC-14): the directive is the declaration, not the check.
- @idempotent mutations require the `Idempotency-Key` header (1–255 visible ASCII), scoped per
  principal; replay returns the original; same key + different payload → IDEMPOTENCY_KEY_REUSED
  (REQ-REG-02).
- @stepUp requires a passkey/TOTP step-up within 5 minutes (REQ-ATT-01, REQ-ADM-13, SEC-09).
- The investor role may only execute persisted operations (REQ-VC-13); introspection is off in
  production (SEC-29); depth and complexity are limited (apps/api/src/limits.ts).
- Errors: GraphQL errors with `extensions.code` from ErrorCode. Private resources the caller may
  not see resolve to null (→ 404 in the web app), never FORBIDDEN, so existence does not leak
  (SEC-14, REQ-ADM-06).
- Time: DateTime is a UTC instant (ISO 8601, Z). LocalDateTime is zone-less wall-clock in the
  accompanying TimeZone. Durations are integers in minutes, never milliseconds.
- Nothing derived, scored, ranked or trended is exposed to founders or investors (REQ-PRG-07).
"""
schema {
  query: Query
  mutation: Mutation
}
Directive@auth

Fail-closed role gate. Required on every Query/Mutation field.

"Fail-closed role gate. Required on every Query/Mutation field."
directive @auth(roles: [Role!]!) on FIELD_DEFINITION | OBJECT
Directive@pii

Contact-class personal data. Must carry a field-level @auth without ANON, HOST or INVESTOR.

"Contact-class personal data. Must carry a field-level @auth without ANON, HOST or INVESTOR."
directive @pii on FIELD_DEFINITION
Directive@personal

Names / free text about a person. Must carry a field-level @auth. `self: true` = only the subject may read it (the resolver returns the caller's own value). Read-audited (Lain F1).

"""
Names / free text about a person. Must carry a field-level @auth. `self: true` = only the subject
may read it (the resolver returns the caller's own value). Read-audited (Lain F1).
"""
directive @personal(self: Boolean = false) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
Directive@visibility

Visibility class of a company/progress/investor-domain field (§4.2). Mirrors the domain registry.

"Visibility class of a company/progress/investor-domain field (§4.2). Mirrors the domain registry."
directive @visibility(class: VisibilityClass!, scope: GrantScope) on FIELD_DEFINITION
Directive@idempotent

Requires the Idempotency-Key header (REQ-REG-02).

"Requires the Idempotency-Key header (REQ-REG-02)."
directive @idempotent on FIELD_DEFINITION
Directive@stepUp

Requires a passkey/TOTP step-up within the last 5 minutes.

"Requires a passkey/TOTP step-up within the last 5 minutes."
directive @stepUp on FIELD_DEFINITION
Directive@audited

Every execution writes an AuditEvent with this action name (SEC-27, REQ-ADM-11, REQ-CNS-02).

"Every execution writes an AuditEvent with this action name (SEC-27, REQ-ADM-11, REQ-CNS-02)."
directive @audited(action: String!) on FIELD_DEFINITION
Directive@rateLimit

Postgres-backed rate limit bucket (T13, SEC-18, SEC-43).

"Postgres-backed rate limit bucket (T13, SEC-18, SEC-43)."
directive @rateLimit(bucket: String!) on FIELD_DEFINITION
ScalarDateTime

UTC instant, ISO 8601 with Z, e.g. 2026-11-01T06:00:00.000Z.

"UTC instant, ISO 8601 with Z, e.g. 2026-11-01T06:00:00.000Z."
scalar DateTime
ScalarLocalDateTime

Zone-less wall-clock time YYYY-MM-DDTHH:mm:ss, interpreted in an accompanying TimeZone.

"Zone-less wall-clock time YYYY-MM-DDTHH:mm:ss, interpreted in an accompanying TimeZone."
scalar LocalDateTime
ScalarLocalDate

Calendar date YYYY-MM-DD.

"Calendar date YYYY-MM-DD."
scalar LocalDate
ScalarTimeZone

IANA time zone name, e.g. America/New_York. Never an offset.

"IANA time zone name, e.g. America/New_York. Never an offset."
scalar TimeZone
ScalarEmailAddress

Email address (input only, except @pii fields).

"Email address (input only, except @pii fields)."
scalar EmailAddress
ScalarURL

Absolute https URL.

"Absolute https URL."
scalar URL
ScalarWebAuthnJSON

Opaque WebAuthn JSON (PublicKeyCredential options/responses), passed through to T3.

"Opaque WebAuthn JSON (PublicKeyCredential options/responses), passed through to T3."
scalar WebAuthnJSON
EnumRole
enum Role {
  ANON
  "A person holding an events-minted manage link (attendee, founder, applicant)."
  ATTENDEE
  "An active member of at least one company."
  FOUNDER
  COMPANY_ADMIN
  HOST
  VIEWER
  EVENT_MANAGER
  PROGRAMME_MANAGER
  ADMIN
  OWNER
  INVESTOR
}
EnumErrorCode
enum ErrorCode {
  FORBIDDEN
  UNAUTHENTICATED
  NOT_FOUND
  NOT_IMPLEMENTED
  VALIDATION_FAILED
  EVENT_FULL
  ILLEGAL_TRANSITION
  IDEMPOTENCY_KEY_REQUIRED
  IDEMPOTENCY_KEY_REUSED
  STALE_VERSION
  REGISTRATION_CLOSED
  BOOKING_LEAD_TIME
  CANCELLATION_WINDOW_PASSED
  REGISTRATION_MODE_LOCKED
  CAPACITY_BELOW_CONFIRMED
  STEP_UP_REQUIRED
  MFA_REQUIRED
  TERMS_NOT_ACCEPTED
  RATE_LIMITED
  PERSISTED_QUERY_REQUIRED
  SOFT_LOCKED
  "Retryable (503): the database was briefly unreachable; retry with the same Idempotency-Key (QA-NEG-20)."
  SERVICE_UNAVAILABLE
}
EnumSeriesKind
enum SeriesKind {
  EVENT
  CLASS
  SLOT
}
EnumEventVisibility
enum EventVisibility {
  PUBLIC
  UNLISTED
  COMMUNITY
  INVITE_ONLY
  INVESTOR
}
EnumRegistrationMode
enum RegistrationMode {
  OPEN
  APPROVAL
  INVITE_ONLY
}
EnumRegistrationScope
enum RegistrationScope {
  OCCURRENCE
  SERIES
}
EnumLocationType
enum LocationType {
  IN_PERSON
  VIRTUAL
  HYBRID
  TBD
}
EnumSeriesStatus
enum SeriesStatus {
  DRAFT
  PUBLISHED
  CANCELLED
  ARCHIVED
}
EnumOccurrenceStatus
enum OccurrenceStatus {
  DRAFT
  PUBLISHED
  CANCELLED
  COMPLETED
}
EnumRegistrationStatus
enum RegistrationStatus {
  PENDING_VERIFICATION
  PENDING_APPROVAL
  PENDING_PAYMENT
  CONFIRMED
  WAITLISTED
  OFFERED
  OFFER_EXPIRED
  "A guest or payment hold lapsed before it was completed (REQ-REG-06). Terminal; the seat was released."
  HOLD_EXPIRED
  DECLINED
  CANCELLED_BY_ATTENDEE
  CANCELLED_BY_ORGANIZER
  ATTENDED
  NO_SHOW
}
EnumRegistrationSource
enum RegistrationSource {
  WEB
  ADMIN
  IMPORT
  WALK_IN
}
EnumOrganizerRole
enum OrganizerRole {
  OWNER
  ADMIN
  EVENT_MANAGER
  PROGRAMME_MANAGER
  HOST
  VIEWER
}
EnumCompanyStatus
enum CompanyStatus {
  PROSPECT
  APPLICANT
  ACTIVE
  ALUMNI
  PAUSED
  WITHDRAWN
}
EnumProgrammeTier
enum ProgrammeTier {
  COMMUNITY
  MIP
  OTHER
}
EnumCompanyMemberRole
enum CompanyMemberRole {
  COMPANY_ADMIN
  EDITOR
  VIEWER
}
EnumProgressUpdateStatus
enum ProgressUpdateStatus {
  DRAFT
  PUBLISHED
  AMENDED
  UNPUBLISHED
}
EnumVisibilityClass

Narrowest first: COMPANY (founders only) < ADMIN_ONLY < INVESTOR < PUBLIC.

"Narrowest first: COMPANY (founders only) < ADMIN_ONLY < INVESTOR < PUBLIC."
enum VisibilityClass {
  COMPANY
  ADMIN_ONLY
  INVESTOR
  PUBLIC
}
EnumProvenance
enum Provenance {
  FOUNDER_REPORTED
  SYSTEM_DERIVED
  OLIVIER_NOTED
  OLIVIER_VERIFIED
}
EnumKpiSource
enum KpiSource {
  FOUNDER_REPORTED
  OLIVIER_VERIFIED
}
EnumMilestoneStatus
enum MilestoneStatus {
  NOT_STARTED
  IN_PROGRESS
  DONE
  BLOCKED
}
EnumMilestoneOrigin
enum MilestoneOrigin {
  PROGRAMME
  COMPANY
}
EnumFundraisingStatus

Coarse enum; no round-terms fields exist anywhere in this schema (SEC-48).

"Coarse enum; no round-terms fields exist anywhere in this schema (SEC-48)."
enum FundraisingStatus {
  NOT_RAISING
  PREPARING
  RAISING
  CLOSING
  CLOSED
}
EnumGrantScope

There is deliberately no ENGAGEMENT scope (REQ-PRG-02, QA-VC-64).

"There is deliberately no ENGAGEMENT scope (REQ-PRG-02, QA-VC-64)."
enum GrantScope {
  PROFILE
  UPDATES
  KPIS
  DECK
  FUNDRAISING_STATUS
  FOUNDER_BIOS
}
EnumGrantAudience
enum GrantAudience {
  ALL_APPROVED_INVESTORS
  ALLOWLIST
}
EnumDisclosureGrantAction
enum DisclosureGrantAction {
  GRANT
  WITHDRAW
  PAUSE
  RESUME
}
EnumConsentPurpose
enum ConsentPurpose {
  INVESTOR_VISIBILITY
  INTRO_CONTACT_REVEAL
  PITCH_SHARE_OLIVIER
  PITCH_SHARE_EVENT
  DIRECTORY_PUBLIC
  ENGAGEMENT_SHARE
  ATTENDEE_LIST_OPT_IN
}
EnumConsentDecision
enum ConsentDecision {
  GRANTED
  WITHDRAWN
}
EnumIntroRequestState
enum IntroRequestState {
  REQUESTED
  OLIVIER_HOLD
  PASSED_TO_FOUNDER
  FOUNDER_HOLD
  FOUNDER_ACCEPTED
  INTRODUCED
  DECLINED_BY_OLIVIER
  DECLINED_BY_FOUNDER
  WITHDRAWN_BY_INVESTOR
}
EnumInvestorIntroStatus

What an investor sees. Olivier- and founder-declines both render CLOSED, with no reason.

"What an investor sees. Olivier- and founder-declines both render CLOSED, with no reason."
enum InvestorIntroStatus {
  AVAILABLE
  REQUESTED
  IN_PROGRESS
  INTRODUCED
  CLOSED
}
EnumInvestorMemberStatus
enum InvestorMemberStatus {
  INVITED
  ACTIVE
  SUSPENDED
  SOFT_LOCKED
  REVOKED
}
EnumInvestorOrgStatus
enum InvestorOrgStatus {
  ACTIVE
  SUSPENDED
  REVOKED
}
EnumApplicationOutcome
enum ApplicationOutcome {
  PENDING
  SELECTED
  NOT_SELECTED
  WAITLISTED
}
EnumPaymentStatus
enum PaymentStatus {
  PENDING
  PAID
  FAILED
  REFUNDED
}
EnumTicketTierKind

Ticket tier role (T9). A label for Olivier's Deal Catch tiers; an INVESTOR ticket confers NO investor-user rights (§3, QA-VC-60).

"Ticket tier role (T9). A label for Olivier's Deal Catch tiers; an INVESTOR ticket confers NO investor-user rights (§3, QA-VC-60)."
enum TicketTierKind {
  GENERAL
  EARLY_BIRD
  FOUNDER
  INVESTOR
  PITCHER
}
EnumRefundStatus

State of one refund attempt (T9, REQ-STM-04/05). NONE on PaymentSummary means no refund was ever started.

"State of one refund attempt (T9, REQ-STM-04/05). NONE on PaymentSummary means no refund was ever started."
enum RefundStatus {
  NONE
  PENDING
  SUCCEEDED
  FAILED
}
EnumRefundPolicyKind

Per-event refund policy for attendee cancellations (REQ-STM-03). Organizer cancellation always refunds in full (REQ-STM-05).

"Per-event refund policy for attendee cancellations (REQ-STM-03). Organizer cancellation always refunds in full (REQ-STM-05)."
enum RefundPolicyKind {
  NO_REFUNDS
  UNTIL_BEFORE_START
}
EnumMessageJobStatus
enum MessageJobStatus {
  SCHEDULED
  CLAIMED
  SENT
  SUPERSEDED
  CANCELLED
  FAILED
  SUPPRESSED
}
EnumAdminFlagKind
enum AdminFlagKind {
  NEEDS_CHECK_IN
  AT_RISK
  PAUSED
}
EnumBreakGlassStatus
enum BreakGlassStatus {
  REQUESTED
  APPROVED
  ACTIVE
  EXPIRED
  REVOKED
}
EnumMaterialAccessLevel
enum MaterialAccessLevel {
  REGISTRANTS
  ATTENDEES
  COMMUNITY
}
EnumPrincipalKind
enum PrincipalKind {
  ANON
  ATTENDEE
  STAFF
  INVESTOR
  OPERATOR
  SYSTEM
}
EnumSignedLinkPurpose
enum SignedLinkPurpose {
  MANAGE_REGISTRATION
  "Guest email verification (REQ-REG-06). Confirms one registration; never exchangeable for a session (#41)."
  VERIFY_REGISTRATION
  MAGIC_SIGN_IN
  STAFF_INVITE
  INVESTOR_INVITE
  FOUNDER_INVITE
  STEP_UP
}
EnumStaffStatus
enum StaffStatus {
  ACTIVE
  SUSPENDED
  REMOVED
}
EnumSuppressionReason
enum SuppressionReason {
  BOUNCE
  COMPLAINT
  ERASURE
  MANUAL
}
EnumEditScope

REQ-ADM-10 edit scope; the admin dialog pre-selects THIS.

"REQ-ADM-10 edit scope; the admin dialog pre-selects THIS."
enum EditScope {
  THIS
  THIS_AND_FOLLOWING
  ALL
}
EnumCancelScope
enum CancelScope {
  "This session only."
  THIS
  "Every session of the class/series."
  ALL_SESSIONS
}
EnumWeekday
enum Weekday {
  MO
  TU
  WE
  TH
  FR
  SA
  SU
}
EnumRecurrencePatternKind
enum RecurrencePatternKind {
  NONE
  DAILY
  WEEKLY
  MONTHLY_BY_DATE
  MONTHLY_BY_WEEKDAY
  CUSTOM_DATES
}
EnumDstResolution

How a wall-clock time crossing a DST transition was resolved (REQ-REC-02/03).

"How a wall-clock time crossing a DST transition was resolved (REQ-REC-02/03)."
enum DstResolution {
  EXACT
  SHIFTED_FORWARD_GAP
  AMBIGUOUS_FIRST_INSTANT
}
EnumAttendanceMark
enum AttendanceMark {
  ATTENDED
  NO_SHOW
  UNKNOWN
}
EnumFounderIntroDecision
enum FounderIntroDecision {
  ACCEPT
  DECLINE
  ASK_OLIVIER_TO_HOLD
}
EnumOlivierIntroAction
enum OlivierIntroAction {
  PASS_TO_FOUNDER
  HOLD
  DECLINE
}
EnumCompanyVisibilityField
enum CompanyVisibilityField {
  FUNDRAISING_STATUS
}
EnumImportSource
enum ImportSource {
  EVENTBRITE
  HUMANITIX
  LUMA
}
EnumImportJobStatus
enum ImportJobStatus {
  UPLOADED
  DRY_RUN_READY
  COMMITTING
  COMMITTED
  FAILED
}
TypePageInfo
type PageInfo {
  endCursor: String
  hasNextPage: Boolean!
}
InputDateRangeInput
input DateRangeInput {
  "Inclusive start instant."
  from: DateTime!
  "Exclusive end instant. At most 13 months after `from` (REQ-REC-07)."
  to: DateTime!
}
TypePublicOrganizer

Organizer branding for public, founder and investor surfaces. Olivier tokens only.

"Organizer branding for public, founder and investor surfaces. Olivier tokens only."
type PublicOrganizer {
  name: String!
  timezone: TimeZone!
  "Olivier theme tokens as CSS custom properties (name → value)."
  themeTokens: [ThemeToken!]!
  "\"Powered by Clouds\" attribution flag; default false (REQ-ADM-18, §7.2(b))."
  poweredByCloudsEnabled: Boolean!
  "Program Year bounds; the recurrence horizon every series end is capped at (REQ-REC-07, UX-12)."
  programYearStart: LocalDate!
  programYearEnd: LocalDate!
}
TypeThemeToken
type ThemeToken {
  name: String!
  value: String!
}
TypePublicOccurrence

What anyone may see about an occurrence. Never attendee names, join links or emails (REQ-ATT-07/08).

"What anyone may see about an occurrence. Never attendee names, join links or emails (REQ-ATT-07/08)."
type PublicOccurrence {
  id: ID!
  seriesId: ID!
  kind: SeriesKind!
  title: String!
  description: String
  "Current notice for enabled managed media, shown before registration (REQ-HST-04); no access credentials."
  mediaPolicyNotice: String
  "Whether the enabled media policy permits host recording; does not imply an active recording or published replay."
  recordingEnabled: Boolean!
  status: OccurrenceStatus!
  visibility: EventVisibility!
  startAt: DateTime!
  endAt: DateTime!
  "Event zone; clients render in the viewer zone with this alongside when different (REQ-REC-05)."
  timezone: TimeZone!
  startLocal: LocalDateTime!
  endLocal: LocalDateTime!
  locationType: LocationType!
  venueName: String
  venueAddress: String
  "Organizer-entered venue city; null when not supplied. Never inferred from the address."
  venueCity: String
  hostDisplayNames: [String!]! @personal @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
  "Count only; never names on public pages (REQ-ATT-08)."
  registeredCount: Int
  capacity: Int
  waitlistEnabled: Boolean!
  registrationMode: RegistrationMode!
  "True when registration is still possible for this viewer."
  registrationOpen: Boolean!
  ticketTiers: [PublicTicketTier!]!
  "Present when the event sells tickets (T9)."
  refundPolicy: RefundPolicy
  "Published, consented pitch line-up only (REQ-VC-14, SEC-47)."
  lineup: PublishedLineup
  """
  The caller's OWN registration status for this occurrence (or its class/series enrolment); null
  when anonymous or not registered. Never anyone else's. Investors resolve through the same
  Attendee identity (investorAttendeeId, REQ-VC-14); being registered grants nothing else.
  """
  myRegistrationStatus: RegistrationStatus
}
TypePublicTicketTier
type PublicTicketTier {
  id: ID!
  name: String!
  kind: TicketTierKind!
  priceCents: Int!
  currency: String!
  soldOut: Boolean!
}
TypeRefundPolicy

Shown on every paid event page before checkout (T9).

"Shown on every paid event page before checkout (T9)."
type RefundPolicy {
  kind: RefundPolicyKind!
  "Elapsed minutes before the start after which attendee cancellations are not refunded."
  windowMinutes: Int
  "Plain-language policy, e.g. \"Full refund if you cancel at least 2 days before the start.\""
  summary: String!
}
TypePublicOccurrenceConnection
type PublicOccurrenceConnection {
  edges: [PublicOccurrenceEdge!]!
  pageInfo: PageInfo!
}
TypePublicOccurrenceEdge
type PublicOccurrenceEdge {
  cursor: String!
  node: PublicOccurrence!
}
TypePublicEventPage

REQ-ADM-06: COMMUNITY events show title, date and a sign-in prompt to non-members (gated = true, details null). INVITE_ONLY, INVESTOR-to-outsiders and drafts resolve to null (404). Cancelled events still resolve (REQ-ATT-13).

"""
REQ-ADM-06: COMMUNITY events show title, date and a sign-in prompt to non-members (gated = true,
details null). INVITE_ONLY, INVESTOR-to-outsiders and drafts resolve to null (404).
Cancelled events still resolve (REQ-ATT-13).
"""
type PublicEventPage {
  gated: Boolean!
  title: String!
  startAt: DateTime!
  timezone: TimeZone!
  occurrence: PublicOccurrence
  "For classes: every session, with this viewer's state."
  sessions: [PublicOccurrence!]
  "Present only for confirmed registrants within the join window (REQ-ATT-07, SEC-17)."
  joinDetails: JoinDetails
  "Co-registrant names: only when the event flag AND each attendee's opt-in are true (REQ-ATT-08)."
  coRegistrantNames: [String!] @personal @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR])
}
TypeJoinDetails
type JoinDetails {
  url: URL! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  availableUntil: DateTime!
}
TypePublishedLineup
type PublishedLineup {
  publishedAt: DateTime!
  slots: [LineupSlot!]!
}
TypeLineupSlot

Company names only — never founders' contacts; no winner/selected badge (REQ-VC-14).

"Company names only — never founders' contacts; no winner/selected badge (REQ-VC-14)."
type LineupSlot {
  position: Int!
  companyName: String!
  startsAt: DateTime
}
TypeBookableSlot

Office Hours slot a member may book (§4.1 SLOT, REQ-ATT-10).

"Office Hours slot a member may book (§4.1 SLOT, REQ-ATT-10)."
type BookableSlot {
  occurrenceId: ID!
  title: String!
  hostDisplayName: String
  startAt: DateTime!
  endAt: DateTime!
  timezone: TimeZone!
  "Last instant a booking is accepted (≥24 h elapsed lead)."
  bookableUntil: DateTime!
  "Last instant a booking may be cancelled (≥12 h elapsed notice)."
  cancellableUntil: DateTime!
  available: Boolean!
}
TypeMe

The signed-in person (events realm). Their own data only.

"The signed-in person (events realm). Their own data only."
type Me {
  id: ID!
  displayName: String! @pii @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  email: String! @pii @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  timezone: TimeZone
  companies: [CompanyMembershipRef!]!
  reminderPreferences: ReminderPreferences!
}
TypeCalendarFeed

A per-person, per-device ICS subscription feed (SEC-16, REQ-EML-09). Feeds exclude join details and attendees; only a token hash is stored; revocable. There are no feed tokens for investors.

"""
A per-person, per-device ICS subscription feed (SEC-16, REQ-EML-09). Feeds exclude join details
and attendees; only a token hash is stored; revocable. There are no feed tokens for investors.
"""
type CalendarFeed {
  id: ID!
  label: String!
  createdAt: DateTime!
  lastFetchedAt: DateTime
  revokedAt: DateTime
}
TypeCalendarFeedCreated
type CalendarFeedCreated {
  feed: CalendarFeed!
  "The subscription URL, shown exactly once (bearer token; never logged)."
  url: URL! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER])
}
TypeCompanyMembershipRef
type CompanyMembershipRef {
  companyId: ID!
  companyName: String!
  role: CompanyMemberRole!
}
TypeReminderPreferences

Cadence control: "manage reminders", never "unsubscribe" (REQ-ATT-05).

"Cadence control: \"manage reminders\", never \"unsubscribe\" (REQ-ATT-05)."
type ReminderPreferences {
  "Elapsed minutes before start, e.g. [1440, 60]."
  leadMinutes: [Int!]!
  enabled: Boolean!
}
TypeCheckInTicket

Private admission ticket, containing no name or email in its signed QR payload.

"Private admission ticket, containing no name or email in its signed QR payload."
type CheckInTicket {
  registrationId: ID!
  occurrenceId: ID!
  occurrence: PublicOccurrence!
  qrToken: String!
}
TypeMyRegistration
type MyRegistration {
  id: ID!
  status: RegistrationStatus!
  occurrence: PublicOccurrence
  series: PublicSeriesSummary!
  """
  Waitlist position, 1-based, while WAITLISTED. Always null in an anonymous `register` response
  (SEC-29): the position is shown once the person opens their verified manage link.
  """
  waitlistPosition: Int
  offerExpiresAt: DateTime
  attendeeListOptIn: Boolean!
  "For class enrolments: sessions opted out of (REQ-ATT-09)."
  optedOutOccurrenceIds: [ID!]!
  "Plain fraction for classes: attended and total sessions so far (REQ-ATT-09)."
  attendedSessions: Int
  totalSessions: Int
  cancellableUntil: DateTime
  payment: PaymentSummary
  createdAt: DateTime!
}
TypePublicSeriesSummary
type PublicSeriesSummary {
  id: ID!
  kind: SeriesKind!
  title: String!
  timezone: TimeZone!
  nextOccurrence: PublicOccurrence
}
TypeMyRegistrationConnection
type MyRegistrationConnection {
  edges: [MyRegistrationEdge!]!
  pageInfo: PageInfo!
}
TypeMyRegistrationEdge
type MyRegistrationEdge {
  cursor: String!
  node: MyRegistration!
}
TypePaymentSummary
type PaymentSummary {
  checkoutStarted: Boolean!
  creditRequested: Boolean!
  creditAppliedCents: Int!
  status: PaymentStatus!
  amountCents: Int!
  currency: String!
  refundedAmountCents: Int!
  "Latest refund attempt (T9)."
  refundStatus: RefundStatus!
}
TypeRegistrationResult
type RegistrationResult {
  registration: MyRegistration!
  """
  True when an existing registration was returned to a signed-in caller (REQ-REG-03: 200, no
  email). Always false for anonymous callers: an existing registration is never revealed in the
  response (SEC-29, #24); its state goes to the address by email instead.
  """
  existing: Boolean!
  "Set while a guest's seat is held pending email verification (REQ-REG-06)."
  holdExpiresAt: DateTime
}
TypeCheckoutSession
type CheckoutSession {
  "Stripe Checkout (hosted) URL on Olivier's connected account (SEC-22)."
  url: URL!
  expiresAt: DateTime!
}
TypeRefundIssue

A refund attempt that failed at Stripe (T9, QA-STM-12). No PII: the admin opens the roster for the person.

"A refund attempt that failed at Stripe (T9, QA-STM-12). No PII: the admin opens the roster for the person."
type RefundIssue {
  registrationId: ID!
  occurrenceId: ID
  attempt: Int!
  status: RefundStatus!
  amountCents: Int!
  currency: String!
  "Stripe's failure_reason code, e.g. expired_or_canceled_card."
  failureCode: String
  createdAt: DateTime!
  updatedAt: DateTime!
}
TypeCohortDirectoryEntry

Static minimum fields of other cohort companies, only if Olivier enables it (REQ-FND-15).

"Static minimum fields of other cohort companies, only if Olivier enables it (REQ-FND-15)."
type CohortDirectoryEntry {
  companyName: String!
  oneLiner: String
  founderDisplayNames: [String!]! @personal @auth(roles: [FOUNDER, COMPANY_ADMIN])
}
InputRegisterInput
input RegisterInput {
  occurrenceId: ID
  "Required for series/class enrolment (SERIES scope)."
  seriesId: ID
  "Guest path: name + email, no password (REQ-ATT-01). Ignored for signed-in people."
  guestName: String @personal
  guestEmail: EmailAddress
  "Required when the event sells tickets. A paid tier holds the seat as PENDING_PAYMENT until checkout (T9)."
  ticketTierId: ID
  "Access code for a hidden tier (T9). Discount codes are applied in startCheckout."
  promoCode: String
  answers: [RegistrationAnswerInput!]
  attendeeListOptIn: Boolean = false
}
InputRegistrationAnswerInput
input RegistrationAnswerInput {
  questionId: ID!
  value: String!
}
InputReminderPreferencesInput
input ReminderPreferencesInput {
  leadMinutes: [Int!]!
  enabled: Boolean!
}
InputConsentInput
input ConsentInput {
  purpose: ConsentPurpose!
  decision: ConsentDecision!
  companyId: ID
  "Field group, intro id or application id the consent is about."
  scopeRef: String
  "Hash of the exact consent text shown (SEC-20)."
  textVersionHash: String!
}
TypeConsentRecord
type ConsentRecord {
  id: ID!
  purpose: ConsentPurpose!
  decision: ConsentDecision!
  scopeRef: String
  textVersionHash: String!
  "False once Olivier has published newer wording for this text: re-prompt softly (QA-CNS-11)."
  textIsCurrent: Boolean!
  createdAt: DateTime!
}
TypeConsentText

One consent wording, versioned; the ledger stores the SHA-256 of `text` (REQ-CNS-03, SEC-20).

"One consent wording, versioned; the ledger stores the SHA-256 of `text` (REQ-CNS-03, SEC-20)."
type ConsentText {
  "Stable id of the checkbox or switch this wording belongs to."
  key: String!
  "Ledger purposes a record citing this wording may carry."
  purposes: [ConsentPurpose!]!
  version: Int!
  textVersionHash: String!
  text: String!
}
TypeSessionInfo
type SessionInfo {
  principalKind: PrincipalKind!
  roles: [Role!]!
  "Second factor verified in this session."
  mfa: Boolean!
  expiresAt: DateTime!
  "True when passkey/TOTP enrolment is still required before any data (SEC-10, SEC-40)."
  enrolmentRequired: Boolean!
}
TypeWebAuthnChallenge
type WebAuthnChallenge {
  challengeId: ID!
  options: WebAuthnJSON!
}
EnumAuthenticatorKind
enum AuthenticatorKind {
  PASSKEY
  TOTP
}
TypeAuthenticator

One of the signed-in person's own second factors (Settings, T7). No secret material.

"One of the signed-in person's own second factors (Settings, T7). No secret material."
type Authenticator {
  id: ID!
  kind: AuthenticatorKind!
  createdAt: DateTime!
  lastUsedAt: DateTime
}
TypeSessionSummary

One of the signed-in person's own sessions (Settings, SEC-42).

"One of the signed-in person's own sessions (Settings, SEC-42)."
type SessionSummary {
  id: ID!
  current: Boolean!
  mfa: Boolean!
  createdAt: DateTime!
  lastSeenAt: DateTime!
  expiresAt: DateTime!
}
TypeTotpEnrolment

TOTP fallback enrolment (SEC-40): shown once, confirmed by verifyTotp. The person's own secret.

"TOTP fallback enrolment (SEC-40): shown once, confirmed by verifyTotp. The person's own secret."
type TotpEnrolment {
  "Base32 shared secret for an authenticator app."
  secret: String!
  otpauthUri: String!
}
TypeEventSeries
type EventSeries {
  id: ID!
  kind: SeriesKind!
  status: SeriesStatus!
  title: String!
  description: String
  visibility: EventVisibility!
  registrationMode: RegistrationMode!
  "Immutable after the first registration (REQ-REC-09…14)."
  registrationScope: RegistrationScope!
  registrationScopeLocked: Boolean!
  locationType: LocationType!
  "Private event-specific video link; null falls back to the venue."
  joinUrl: URL @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  venue: Venue
  timezone: TimeZone!
  dtstartLocal: LocalDateTime!
  durationMinutes: Int!
  recurrence: Recurrence
  "A clone with no dates yet (REQ-ADM-05): the schedule shown is the source's, as a template; nothing is materialised or publishable until a recurrence is saved."
  datesPending: Boolean!
  "null = unlimited (never 0)."
  capacity: Int
  waitlistEnabled: Boolean!
  offerWindowMinutes: Int!
  registrationClosesBeforeStartMinutes: Int
  cancellationCutoffMinutes: Int
  joinDetailsWindowMinutes: Int!
  sensitiveParticipation: Boolean!
  attendeeListEnabled: Boolean!
  requiresApplication: Boolean!
  eventBrief: EventBrief
  tags: [String!]!
  hosts: [Host!]!
  questions: [RegistrationQuestion!]!
  ticketTiers: [TicketTier!]!
  refundPolicy: RefundPolicy!
  occurrences(range: DateRangeInput, first: Int = 100, after: String): OccurrenceConnection!
  "Optimistic concurrency token; send back as expectedVersion (409 STALE_VERSION)."
  version: Int!
  createdAt: DateTime!
  updatedAt: DateTime!
  publishedAt: DateTime
}
TypeRecurrence
type Recurrence {
  pattern: RecurrencePatternKind!
  interval: Int
  weekdays: [Weekday!]
  dayOfMonth: Int
  weekdayOrdinal: Int
  "End condition: exactly one of untilLocal / count (no \"Never\", REQ-REC-07)."
  untilLocal: LocalDateTime
  count: Int
  exdatesLocal: [LocalDateTime!]!
  "Live read-back sentence (REQ-ADM-03)."
  description: String!
  "RFC 5545 lines for ICS (REQ-REC-06)."
  rfc5545: [String!]!
}
TypeEventBrief

Schedule E Event Brief [CONTRACT §6.2].

"Schedule E Event Brief [CONTRACT §6.2]."
type EventBrief {
  afResponsibility: String
  materialsDueAt: DateTime
  notes: String
}
TypeHost
type Host {
  id: ID!
  displayName: String!
  staffUserId: ID
  occurrenceId: ID
}
TypeRegistrationQuestion
type RegistrationQuestion {
  id: ID!
  label: String!
  kind: String!
  options: [String!]!
  required: Boolean!
  position: Int!
}
TypeTicketTier
type TicketTier {
  id: ID!
  name: String!
  kind: TicketTierKind!
  "Hidden from the public page; sold only with an access code that unlocks it."
  requiresAccessCode: Boolean!
  priceCents: Int!
  currency: String!
  capacity: Int
  salesStartAt: DateTime
  salesEndAt: DateTime
  position: Int!
}
TypePromoCode
type PromoCode {
  id: ID!
  percentOff: Int
  amountOffCents: Int
  unlocksTierId: ID
  maxRedemptions: Int
  redemptions: Int!
  expiresAt: DateTime
}
TypeVenue
type Venue {
  id: ID!
  name: String!
  kind: LocationType!
  address: String
  city: String
  hasJoinUrl: Boolean!
}
TypeOccurrence
type Occurrence {
  id: ID!
  series: EventSeries!
  status: OccurrenceStatus!
  originalStartLocal: LocalDateTime!
  startLocal: LocalDateTime!
  endLocal: LocalDateTime!
  timezone: TimeZone!
  startAt: DateTime!
  endAt: DateTime!
  dst: DstResolution!
  isException: Boolean!
  "Has its own time (a THIS edit); keeps it through ALL edits unless exceptions are reset (REQ-ADM-10)."
  timeOverridden: Boolean!
  "ICS SEQUENCE; +1 on each temporal/location change of a published occurrence (REQ-REC-12)."
  sequence: Int!
  title: String!
  description: String
  "Effective capacity (override or series)."
  capacity: Int
  capacityOverridden: Boolean!
  venue: Venue
  locationType: LocationType!
  "Private session override; null inherits the series or venue link."
  joinUrl: URL @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  hosts: [Host!]!
  materials: [Material!]!
  materialsDueAt: DateTime
  recordingAccess: MaterialAccessLevel
  attendanceClosed: Boolean!
  counts: OccurrenceCounts!
  version: Int!
}
TypeOccurrenceCounts

Per-event counts (REQ-ADM-16). Staff only; never on founder/investor surfaces.

"Per-event counts (REQ-ADM-16). Staff only; never on founder/investor surfaces."
type OccurrenceCounts {
  registered: Int!
  waitlisted: Int!
  pendingApproval: Int!
  attended: Int!
  noShow: Int!
  unknown: Int!
  "Attended / (attended + no-show), with its denominator; null before attendance is taken."
  showRateNumerator: Int
  showRateDenominator: Int
}
TypeOccurrenceConnection
type OccurrenceConnection {
  edges: [OccurrenceEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}
TypeOccurrenceEdge
type OccurrenceEdge {
  cursor: String!
  node: Occurrence!
}
InputCalendarFilter
input CalendarFilter {
  kinds: [SeriesKind!]
  statuses: [OccurrenceStatus!]
  visibilities: [EventVisibility!]
  hostStaffUserIds: [ID!]
  tags: [String!]
  "Title search."
  search: String
  "Office Hours as a toggleable layer (REQ-ADM-02)."
  includeOfficeHours: Boolean = true
}
TypeMaterial
type Material {
  id: ID!
  title: String!
  contentType: String!
  sizeBytes: Int!
  availableFrom: DateTime
  accessLevel: MaterialAccessLevel!
  uploadedAt: DateTime!
  dueAt: DateTime
  "Uploaded after the 3-business-day due date [CONTRACT §6.1]."
  late: Boolean!
  "Short-lived authenticated URL (private storage, REQ-HST-02)."
  downloadUrl: URL!
}
TypeRosterEntry

Roster row (REQ-ADM-11, REQ-HST-01). Hosts see displayName, status and attendance only; email is masked and revealed one row at a time via revealRegistrantEmail (audited).

"""
Roster row (REQ-ADM-11, REQ-HST-01). Hosts see displayName, status and attendance only; email is
masked and revealed one row at a time via revealRegistrantEmail (audited).
"""
type RosterEntry {
  registrationId: ID!
  "Expected signed ticket for offline scanning; only for an active occurrence registration."
  qrToken: String @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
  displayName: String! @personal @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
  status: RegistrationStatus!
  registeredAt: DateTime!
  attendance: AttendanceMark!
  source: RegistrationSource!
  "e.g. a***@example.com"
  maskedEmail: String! @pii @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  answers: [RegistrationAnswer!] @pii @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  ticketTierName: String
}
TypeRegistrationAnswer
type RegistrationAnswer {
  questionId: ID!
  label: String!
  value: String!
}
TypeRosterConnection
type RosterConnection {
  edges: [RosterEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}
TypeRosterEdge
type RosterEdge {
  cursor: String!
  node: RosterEntry!
}
TypeAttendanceMatrix

Per-class attendance matrix (REQ-ADM-12).

"Per-class attendance matrix (REQ-ADM-12)."
type AttendanceMatrix {
  sessions: [Occurrence!]!
  rows: [AttendanceMatrixRow!]!
}
TypeAttendanceMatrixRow
type AttendanceMatrixRow {
  registrationId: ID!
  displayName: String! @personal @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
  marks: [AttendanceMark!]!
}
TypeRosterExport

Owner-only roster export (REQ-ADM-13): step-up, rate-limited, audited, other Owners notified.

"Owner-only roster export (REQ-ADM-13): step-up, rate-limited, audited, other Owners notified."
type RosterExport {
  "Short-lived authenticated download URL for the CSV."
  url: URL!
  rowCount: Int!
  expiresAt: DateTime!
}
TypeApprovalItem
type ApprovalItem {
  registration: RosterEntry!
  application: Application
}
TypePublishPreflight
type PublishPreflight {
  ok: Boolean!
  problems: [String!]!
  "e.g. \"Notify 23 attendees\" for temporal edits (REQ-ADM-09)."
  affectedRegistrants: Int!
}
TypeEditResult
type EditResult {
  series: EventSeries!
  affectedOccurrenceIds: [ID!]!
  "Registrants who will be emailed; restated for ALL-scope temporal edits (REQ-ADM-10)."
  notifiedRegistrants: Int!
  "Occurrences that keep their own time through this edit (\"1 occurrence has its own time and will keep it\", QA-EDT-05)."
  keptOverrideOccurrenceIds: [ID!]!
  "True when nothing was written (dryRun: the impact shown before the admin confirms)."
  dryRun: Boolean!
  "Send the update emails after a 10 s undo window unless undone (REQ-ADM-09)."
  pendingNotificationId: ID
}
TypeRecurrencePreview
type RecurrencePreview {
  description: String!
  occurrences: [PreviewOccurrence!]!
  warnings: [String!]!
  errors: [String!]!
}
TypePreviewOccurrence
type PreviewOccurrence {
  originalStartLocal: LocalDateTime!
  startLocal: LocalDateTime!
  endLocal: LocalDateTime!
  startAt: DateTime!
  endAt: DateTime!
  dst: DstResolution!
}
TypeComplianceMonth

§5.3 / §6.1 counters, surfaced not enforced (REQ-ADM-17).

"§5.3 / §6.1 counters, surfaced not enforced (REQ-ADM-17)."
type ComplianceMonth {
  month: LocalDate!
  asOf: DateTime!
  slotsPublished: Int!
  slotBookings: Int!
  teachingSessionsDelivered: Int!
  lateMaterials: Int!
  automatic: ComplianceCounts!
  reported: ComplianceCounts!
  "All active app bookings, for context only; Partner attribution is not inferred."
  appBookings: Int!
  activities: [ComplianceActivity!]!
}
EnumComplianceMetric
enum ComplianceMetric {
  SLOTS_PUBLISHED
  PARTNER_BOOKINGS
  TEACHING_SESSIONS
  LATE_MATERIALS
}
EnumComplianceSource
enum ComplianceSource {
  APP
  REPORTED
}
TypeComplianceCounts
type ComplianceCounts {
  slotsPublished: Int!
  slotBookings: Int!
  teachingSessionsDelivered: Int!
  lateMaterials: Int!
}
TypeComplianceActivity
type ComplianceActivity {
  id: ID!
  metric: ComplianceMetric!
  date: LocalDate!
  title: String! @personal @auth(roles: [OWNER, ADMIN])
  notes: String @personal @auth(roles: [OWNER, ADMIN])
  source: ComplianceSource!
  occurrenceId: ID
}
InputComplianceActivityInput
input ComplianceActivityInput {
  "Stable client-generated UUID; retries do not increment the total again."
  id: ID!
  metric: ComplianceMetric!
  date: LocalDate!
  title: String!
  notes: String!
  "Required for reported teaching sessions; at least 45 minutes."
  durationMinutes: Int
}
TypeCheckInSyncResult
type CheckInSyncResult {
  applied: Int!
  duplicates: Int!
  rejected: [CheckInRejection!]!
}
TypeCheckInRejection
type CheckInRejection {
  clientId: ID!
  reason: String!
}
TypeUploadTicket
type UploadTicket {
  "Pre-signed PUT URL to private storage; ≤5 min."
  uploadUrl: URL!
  objectId: ID!
  expiresAt: DateTime!
}
TypeImportJob
type ImportJob {
  id: ID!
  source: ImportSource!
  status: ImportJobStatus!
  "PII-free dry-run report (REQ-MIG-06/07)."
  rowsTotal: Int
  rowsNew: Int
  rowsDuplicate: Int
  rowsRejected: Int
  "Line numbers and reason codes only, e.g. \"file 1 line 57: MISSING_EMAIL\" — never a cell value."
  errors: [String!]!
  createdAt: DateTime!
  "(T11) Rows whose content hash was imported before: re-import is idempotent (REQ-MIG-05)."
  rowsAlreadyImported: Int
  "(T11) Check-in rows that mark attendance on an existing registration (REQ-MIG-03)."
  rowsUpdated: Int
  "(T11) Rows parsed so far in the current phase (QA-MIG-13 progress)."
  rowsProcessed: Int!
  "(T11) Events the commit creates."
  eventsToCreate: Int
  waitlist: Int
  "(T11) Same title on several dates: offer \"link as a series\" — an admin action, never automatic (REQ-MIG-08)."
  seriesSuggestions: [ImportSeriesSuggestion!]!
  "(T11) Single-use ticket for the next upload phase; the same bytes are sent again to commit (REQ-MIG-06)."
  uploadTicket: UploadTicket
  "(T11) Why the job failed, as a code (never row content)."
  failureCode: String
  committedAt: DateTime
}
TypeImportSeriesSuggestion
type ImportSeriesSuggestion {
  title: String!
  occurrences: Int!
}
InputRecurrenceInput
input RecurrenceInput {
  timezone: TimeZone!
  dtstartLocal: LocalDateTime!
  durationMinutes: Int!
  pattern: RecurrencePatternKind!
  interval: Int
  weekdays: [Weekday!]
  dayOfMonth: Int
  weekdayOrdinal: Int
  customStartsLocal: [LocalDateTime!]
  untilLocal: LocalDateTime
  count: Int
  exdatesLocal: [LocalDateTime!]
  "SLOT only: carve each availability window (dtstart + durationMinutes) into slots of this length."
  slotMinutes: Int
  "SLOT only: gap between consecutive slots."
  slotBufferMinutes: Int
}
InputEventSeriesInput
input EventSeriesInput {
  kind: SeriesKind!
  title: String!
  description: String
  visibility: EventVisibility = COMMUNITY
  registrationMode: RegistrationMode = OPEN
  registrationScope: RegistrationScope = OCCURRENCE
  locationType: LocationType = TBD
  joinUrl: URL
  venueId: ID
  recurrence: RecurrenceInput!
  capacity: Int
  waitlistEnabled: Boolean = false
  offerWindowMinutes: Int
  registrationClosesBeforeStartMinutes: Int
  cancellationCutoffMinutes: Int
  joinDetailsWindowMinutes: Int
  sensitiveParticipation: Boolean
  attendeeListEnabled: Boolean = false
  requiresApplication: Boolean = false
  tags: [String!]
  hostStaffUserIds: [ID!]
  guestHostNames: [String!]
}
InputEventSeriesPatch
input EventSeriesPatch {
  joinUrl: URL
  joinDetailsWindowMinutes: Int
  title: String
  description: String
  visibility: EventVisibility
  registrationMode: RegistrationMode
  locationType: LocationType
  venueId: ID
  recurrence: RecurrenceInput
  capacity: Int
  waitlistEnabled: Boolean
  offerWindowMinutes: Int
  registrationClosesBeforeStartMinutes: Int
  cancellationCutoffMinutes: Int
  sensitiveParticipation: Boolean
  attendeeListEnabled: Boolean
  tags: [String!]
}
InputOccurrencePatch
input OccurrencePatch {
  startLocal: LocalDateTime
  endLocal: LocalDateTime
  title: String
  description: String
  venueId: ID
  locationType: LocationType
  joinUrl: URL
  capacity: Int
}
InputEventBriefInput
input EventBriefInput {
  afResponsibility: String
  materialsDueAt: DateTime
  notes: String
}
InputManualRegistrationInput
input ManualRegistrationInput {
  occurrenceId: ID
  seriesId: ID!
  name: String!
  email: EmailAddress!
  "Manual add obeys the capacity invariant (REQ-CAP-07)."
  status: RegistrationStatus = CONFIRMED
  "Required to book an Office Hours slot inside the 24 h lead on someone's behalf; audited (REQ-ATT-10)."
  overrideReason: String
}
InputAttendanceInput
input AttendanceInput {
  registrationId: ID!
  mark: AttendanceMark!
}
InputWalkInInput
input WalkInInput {
  name: String!
  email: EmailAddress
}
InputCheckInSyncItem
input CheckInSyncItem {
  "Client-generated id; replays are idempotent (REQ-ADM-20)."
  clientId: ID!
  registrationId: ID
  walkIn: WalkInInput
  occurrenceId: ID!
  "Signed QR payload, if scanned."
  qrToken: String
  markedAt: DateTime!
}
InputVenueInput
input VenueInput {
  name: String!
  kind: LocationType!
  address: String
  city: String
  joinUrl: URL
}
InputTicketTierInput
input TicketTierInput {
  seriesId: ID!
  name: String!
  kind: TicketTierKind = GENERAL
  requiresAccessCode: Boolean = false
  priceCents: Int!
  currency: String = "usd"
  capacity: Int
  salesStartAt: DateTime
  salesEndAt: DateTime
  position: Int
}
InputPromoCodeInput
input PromoCodeInput {
  seriesId: ID!
  code: String!
  percentOff: Int
  amountOffCents: Int
  unlocksTierId: ID
  maxRedemptions: Int
  expiresAt: DateTime
}
InputRefundPolicyInput
input RefundPolicyInput {
  kind: RefundPolicyKind!
  "Required for UNTIL_BEFORE_START; 0 = any time before the start."
  windowMinutes: Int
}
InputMaterialUploadInput
input MaterialUploadInput {
  occurrenceId: ID
  seriesId: ID
  title: String!
  contentType: String!
  sizeBytes: Int!
  availableFrom: DateTime
  accessLevel: MaterialAccessLevel = REGISTRANTS
}
InputImportInput
input ImportInput {
  source: ImportSource!
  "Upload object id from requestImportUpload; the file is streamed, never persisted (REQ-MIG-06)."
  objectId: ID!
  "Required when the file has no zone column (REQ-MIG-04)."
  timezone: TimeZone
  seriesId: ID
}
TypeOrganizerSettings
type OrganizerSettings {
  name: String!
  timezone: TimeZone!
  programYearStart: LocalDate!
  programYearEnd: LocalDate!
  senderDomain: String
  replyToAddress: String
  themeTokens: [ThemeToken!]!
  poweredByCloudsEnabled: Boolean!
  waitlistOfferWindowMinutes: Int!
  reminderLeadMinutes: [Int!]!
  cohortDirectoryEnabled: Boolean!
  publicDirectoryEnabled: Boolean!
  visibilityPolicy: VisibilityPolicy!
}
TypeVisibilityPolicy

Organizer visibility policy (REQ-CNS-06). Engagement ceiling is ADMIN_ONLY in v1.

"Organizer visibility policy (REQ-CNS-06). Engagement ceiling is ADMIN_ONLY in v1."
type VisibilityPolicy {
  profileCeiling: VisibilityClass!
  programmeStatusCeiling: VisibilityClass!
  teamCeiling: VisibilityClass!
  updatesCeiling: VisibilityClass!
  kpisCeiling: VisibilityClass!
  milestonesCeiling: VisibilityClass!
  deckCeiling: VisibilityClass!
  fundraisingCeiling: VisibilityClass!
  engagementCeiling: VisibilityClass!
  kThreshold: Int!
  investorCompanyViewsPerHour: Int!
  investorCompanyViewsPerDay: Int!
  investorPageSize: Int!
  introRequestsPerMonth: Int!
  byNameFollowerPolicy: Boolean!
  sectorVocabulary: [String!]!
  stageVocabulary: [String!]!
  version: Int!
}
TypeStaffMember
type StaffMember {
  id: ID!
  displayName: String! @personal @auth(roles: [OWNER, ADMIN])
  email: String! @pii @auth(roles: [OWNER, ADMIN])
  status: StaffStatus!
  roles: [OrganizerRole!]!
  mfaEnrolled: Boolean!
  "Matches the AF-affiliated identity list; Owners were notified (REQ-ADM-14, QA-ADM-30)."
  afAffiliated: Boolean!
  lastSignInAt: DateTime
}
TypeAuditEvent
type AuditEvent {
  id: ID!
  occurredAt: DateTime!
  actorKind: PrincipalKind!
  actorDisplayName: String @personal @auth(roles: [OWNER, ADMIN])
  actorRole: String
  category: String!
  action: String!
  outcome: String!
  subjectType: String
  subjectId: ID
  rowCount: Int
  reason: String @personal @auth(roles: [OWNER, ADMIN])
}
TypeAuditEventConnection
type AuditEventConnection {
  edges: [AuditEventEdge!]!
  pageInfo: PageInfo!
}
TypeAuditEventEdge
type AuditEventEdge {
  cursor: String!
  node: AuditEvent!
}
InputAuditFilter
input AuditFilter {
  range: DateRangeInput
  categories: [String!]
  actorKinds: [PrincipalKind!]
  companyId: ID
}
InputOrganizerSettingsInput
input OrganizerSettingsInput {
  name: String
  timezone: TimeZone
  programYearEnd: LocalDate
  replyToAddress: String
  themeTokens: [ThemeTokenInput!]
  poweredByCloudsEnabled: Boolean
  waitlistOfferWindowMinutes: Int
  reminderLeadMinutes: [Int!]
  cohortDirectoryEnabled: Boolean
  publicDirectoryEnabled: Boolean
}
InputThemeTokenInput
input ThemeTokenInput {
  name: String!
  value: String!
}
InputVisibilityPolicyInput
input VisibilityPolicyInput {
  profileCeiling: VisibilityClass
  programmeStatusCeiling: VisibilityClass
  teamCeiling: VisibilityClass
  updatesCeiling: VisibilityClass
  kpisCeiling: VisibilityClass
  milestonesCeiling: VisibilityClass
  deckCeiling: VisibilityClass
  fundraisingCeiling: VisibilityClass
  kThreshold: Int
  investorCompanyViewsPerHour: Int
  investorCompanyViewsPerDay: Int
  introRequestsPerMonth: Int
  byNameFollowerPolicy: Boolean
  sectorVocabulary: [String!]
  stageVocabulary: [String!]
  expectedVersion: Int!
}
TypeCohortIntake
type CohortIntake {
  id: ID!
  name: String!
  startsOn: LocalDate!
  endsOn: LocalDate
  accessGroups: [String!]!
}
TypeCompany

Company as Olivier staff and its own founders see it. Other founders never see this type (SEC-46).

"Company as Olivier staff and its own founders see it. Other founders never see this type (SEC-46)."
type Company {
  id: ID! @visibility(class: PUBLIC, scope: PROFILE)
  name: String! @visibility(class: PUBLIC, scope: PROFILE)
  slug: String! @visibility(class: PUBLIC, scope: PROFILE)
  logoUrl: URL @visibility(class: PUBLIC, scope: PROFILE)
  oneLiner: String @visibility(class: PUBLIC, scope: PROFILE)
  sector: String @visibility(class: PUBLIC, scope: PROFILE)
  description: String @visibility(class: INVESTOR, scope: PROFILE)
  stage: String @visibility(class: INVESTOR, scope: PROFILE)
  website: URL @visibility(class: INVESTOR, scope: PROFILE)
  location: String @visibility(class: INVESTOR, scope: PROFILE)
  foundedYear: Int @visibility(class: INVESTOR, scope: PROFILE)
  status: CompanyStatus! @visibility(class: INVESTOR, scope: PROFILE)
  intakes: [CohortIntake!]! @visibility(class: INVESTOR, scope: PROFILE)
  programmeTier: ProgrammeTier! @visibility(class: ADMIN_ONLY)
  fundraisingStatus: FundraisingStatus @visibility(class: INVESTOR, scope: FUNDRAISING_STATUS)
  fundraisingVisibility: VisibilityClass! @visibility(class: ADMIN_ONLY)
  "Founder toggle; nothing is investor-visible until this AND a grant (REQ-FND-01)."
  visibleToInvestors: Boolean! @visibility(class: ADMIN_ONLY)
  lastUpdatePublishedAt: DateTime @visibility(class: INVESTOR, scope: UPDATES)
  "Olivier sees at most a count of drafts in progress, never content (Q-OL-31)."
  draftInProgressCount: Int! @visibility(class: ADMIN_ONLY)
  team: [CompanyTeamEntry!]! @visibility(class: INVESTOR, scope: FOUNDER_BIOS)
  members: [CompanyMember!]! @visibility(class: ADMIN_ONLY)
  updates(first: Int = 12, after: String): ProgressUpdateConnection! @visibility(class: INVESTOR, scope: UPDATES)
  "Founders only — absent for Olivier (REQ-FND-03)."
  drafts: [ProgressUpdateDraft!]! @visibility(class: COMPANY)
  milestones: [Milestone!]! @visibility(class: INVESTOR, scope: UPDATES)
  kpiDefinitions: [KpiDefinition!]! @visibility(class: INVESTOR, scope: KPIS)
  pitchMaterials: [PitchMaterial!]! @visibility(class: INVESTOR, scope: DECK)
  "Programme-derived, admin + own company only in v1 (REQ-PRG-01/02, REQ-FND-09)."
  engagement(first: Int = 12): [EngagementSnapshot!]! @visibility(class: ADMIN_ONLY)
  adminNotes: [AdminNote!]! @visibility(class: ADMIN_ONLY)
  adminFlags: [AdminFlag!]! @visibility(class: ADMIN_ONLY)
  grants: [DisclosureGrant!]! @visibility(class: ADMIN_ONLY)
  founderBlocks: [FounderBlock!]! @visibility(class: ADMIN_ONLY)
  introRequests(first: Int = 20, after: String): IntroRequestConnection! @visibility(class: ADMIN_ONLY)
  "Aggregate only, k-floored, no names or timestamps (REQ-FND-13, C)."
  investorInterest: InvestorInterest @visibility(class: ADMIN_ONLY)
  "Founder reminder cadence (REQ-PRG-05, REQ-FND-07). Never \"overdue\" copy."
  updateCadence: UpdateCadence @visibility(class: ADMIN_ONLY)
  createdAt: DateTime! @visibility(class: ADMIN_ONLY)
}
TypeUpdateCadence

Update cadence as the company sees it (REQ-PRG-05, REQ-FND-07). Founders may lengthen in whole 30-day months, never below Olivier's tier minimum. One reminder per cycle, three days before `windowClosesOn`.

"""
Update cadence as the company sees it (REQ-PRG-05, REQ-FND-07). Founders may lengthen in whole
30-day months, never below Olivier's tier minimum. One reminder per cycle, three days before
`windowClosesOn`.
"""
type UpdateCadence {
  "Effective cadence in whole 30-day months."
  months: Int!
  minimumMonths: Int!
  "Months the founder may choose (≥ minimum)."
  options: [Int!]!
  "Effective cadence in days (the reminder scheduler's unit)."
  cadenceDays: Int!
  minimumDays: Int!
  "The current cycle ends at local midnight (organizer zone) on this date; null before the Program Year starts."
  windowClosesOn: LocalDate
  snoozedThisCycle: Boolean!
}
TypeCompanyConnection
type CompanyConnection {
  edges: [CompanyEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}
TypeCompanyEdge
type CompanyEdge {
  cursor: String!
  node: Company!
}
TypeCompanyTeamEntry

Team section: name, role, one line, optional LinkedIn. No email, phone or photo fields exist.

"Team section: name, role, one line, optional LinkedIn. No email, phone or photo fields exist."
type CompanyTeamEntry {
  id: ID! @visibility(class: INVESTOR, scope: FOUNDER_BIOS)
  displayName: String! @visibility(class: INVESTOR, scope: FOUNDER_BIOS)
  role: String @visibility(class: INVESTOR, scope: FOUNDER_BIOS)
  oneLine: String @visibility(class: INVESTOR, scope: FOUNDER_BIOS)
  linkedinUrl: URL @visibility(class: INVESTOR, scope: FOUNDER_BIOS)
  visibility: VisibilityClass! @visibility(class: ADMIN_ONLY)
}
TypeCompanyMember
type CompanyMember {
  id: ID! @visibility(class: ADMIN_ONLY)
  role: CompanyMemberRole! @visibility(class: ADMIN_ONLY)
  displayName: String! @visibility(class: ADMIN_ONLY) @personal @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER, FOUNDER, COMPANY_ADMIN])
  email: String! @visibility(class: ADMIN_ONLY) @pii @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER, COMPANY_ADMIN])
  startedAt: DateTime! @visibility(class: ADMIN_ONLY)
  endedAt: DateTime @visibility(class: ADMIN_ONLY)
}
TypeProgressUpdate
type ProgressUpdate {
  id: ID! @visibility(class: INVESTOR, scope: UPDATES)
  period: LocalDate! @visibility(class: INVESTOR, scope: UPDATES)
  title: String! @visibility(class: INVESTOR, scope: UPDATES)
  publishedAt: DateTime @visibility(class: INVESTOR, scope: UPDATES)
  "Set when amended; investors see \"Edited <date>\", never a diff (REQ-FND-03)."
  editedAt: DateTime @visibility(class: INVESTOR, scope: UPDATES)
  versionNo: Int! @visibility(class: ADMIN_ONLY)
  status: ProgressUpdateStatus! @visibility(class: ADMIN_ONLY)
  whatHappened: String @visibility(class: INVESTOR, scope: UPDATES)
  whatsNext: String @visibility(class: INVESTOR, scope: UPDATES)
  asks: String @visibility(class: INVESTOR, scope: UPDATES)
  lowlights: String @visibility(class: INVESTOR, scope: UPDATES)
  "Always admin-only; no toggle."
  asksOfOlivier: String @visibility(class: ADMIN_ONLY)
  "Always admin-only; no toggle (REQ-FND-02)."
  notesForOlivier: String @visibility(class: ADMIN_ONLY)
  kpiValues: [KpiValue!]! @visibility(class: INVESTOR, scope: KPIS)
  sectionVisibility: SectionVisibility! @visibility(class: ADMIN_ONLY)
  "Shown to the founder too (REQ-CO-06)."
  unpublishReason: String @visibility(class: ADMIN_ONLY)
  provenance: Provenance! @visibility(class: INVESTOR, scope: UPDATES)
}
TypeSectionVisibility
type SectionVisibility {
  whatHappened: VisibilityClass!
  whatsNext: VisibilityClass!
  lowlights: VisibilityClass!
  asks: VisibilityClass!
  kpis: VisibilityClass!
}
TypeProgressUpdateConnection
type ProgressUpdateConnection {
  edges: [ProgressUpdateEdge!]!
  pageInfo: PageInfo!
}
TypeProgressUpdateEdge
type ProgressUpdateEdge {
  cursor: String!
  node: ProgressUpdate!
}
TypeProgressUpdateDraft

Founder-only draft; Olivier cannot read it (REQ-FND-03).

"Founder-only draft; Olivier cannot read it (REQ-FND-03)."
type ProgressUpdateDraft {
  id: ID! @visibility(class: COMPANY)
  period: LocalDate! @visibility(class: COMPANY)
  title: String @visibility(class: COMPANY)
  whatHappened: String @visibility(class: COMPANY)
  whatsNext: String @visibility(class: COMPANY)
  lowlights: String @visibility(class: COMPANY)
  asks: String @visibility(class: COMPANY)
  asksOfOlivier: String @visibility(class: COMPANY)
  notesForOlivier: String @visibility(class: COMPANY)
  kpiValues: [KpiValue!]! @visibility(class: COMPANY)
  sectionVisibility: SectionVisibility! @visibility(class: COMPANY)
  updatedAt: DateTime! @visibility(class: COMPANY)
}
TypeKpiDefinition
type KpiDefinition {
  id: ID! @visibility(class: INVESTOR, scope: KPIS)
  name: String! @visibility(class: INVESTOR, scope: KPIS)
  unit: String @visibility(class: INVESTOR, scope: KPIS)
  position: Int! @visibility(class: INVESTOR, scope: KPIS)
  isStandard: Boolean! @visibility(class: INVESTOR, scope: KPIS)
  visibility: VisibilityClass! @visibility(class: ADMIN_ONLY)
}
TypeKpiValue

A founder-entered value. Never computed: no deltas, growth or trends (REQ-FND-04, REQ-PRG-07).

"A founder-entered value. Never computed: no deltas, growth or trends (REQ-FND-04, REQ-PRG-07)."
type KpiValue {
  id: ID! @visibility(class: INVESTOR, scope: KPIS)
  name: String! @visibility(class: INVESTOR, scope: KPIS)
  "As entered (decimal string), with unit."
  value: String @visibility(class: INVESTOR, scope: KPIS)
  unit: String @visibility(class: INVESTOR, scope: KPIS)
  asOf: LocalDate! @visibility(class: INVESTOR, scope: KPIS)
  source: KpiSource! @visibility(class: INVESTOR, scope: KPIS)
}
TypeMilestone
type Milestone {
  id: ID! @visibility(class: INVESTOR, scope: UPDATES)
  title: String! @visibility(class: INVESTOR, scope: UPDATES)
  status: MilestoneStatus! @visibility(class: INVESTOR, scope: UPDATES)
  origin: MilestoneOrigin! @visibility(class: INVESTOR, scope: UPDATES)
  dueDate: LocalDate @visibility(class: INVESTOR, scope: UPDATES)
  completedOn: LocalDate @visibility(class: INVESTOR, scope: UPDATES)
  visibility: VisibilityClass! @visibility(class: ADMIN_ONLY)
}
TypePitchMaterial
type PitchMaterial {
  id: ID! @visibility(class: INVESTOR, scope: DECK)
  version: Int! @visibility(class: INVESTOR, scope: DECK)
  pageCount: Int @visibility(class: INVESTOR, scope: DECK)
  uploadedAt: DateTime! @visibility(class: INVESTOR, scope: DECK)
  downloadAllowed: Boolean! @visibility(class: INVESTOR, scope: DECK)
  watermarkRequired: Boolean! @visibility(class: ADMIN_ONLY)
  visibility: VisibilityClass! @visibility(class: ADMIN_ONLY)
  sharedWithApplicationIds: [ID!]! @visibility(class: ADMIN_ONLY)
  "Watermarked page images via ≤5-min authenticated URLs (SEC-44)."
  pages: [DeckPageImage!]! @visibility(class: INVESTOR, scope: DECK)
  "Distinct investors who viewed this file in 30 days; null below the k-threshold (REQ-FND-13). Never names."
  viewedBy30d: Int @visibility(class: ADMIN_ONLY)
}
TypeDeckPageImage
type DeckPageImage {
  number: Int!
  imageUrl: URL!
  expiresAt: DateTime!
  altText: String
}
TypeEngagementSnapshot

Derived, never edited, no composite score (REQ-PRG-01). Counts with denominators.

"Derived, never edited, no composite score (REQ-PRG-01). Counts with denominators."
type EngagementSnapshot {
  id: ID! @visibility(class: ADMIN_ONLY)
  periodStart: LocalDate! @visibility(class: ADMIN_ONLY)
  periodEnd: LocalDate! @visibility(class: ADMIN_ONLY)
  sessionsRegistered: Int! @visibility(class: ADMIN_ONLY)
  sessionsAttended: Int! @visibility(class: ADMIN_ONLY)
  officeHoursBooked: Int! @visibility(class: ADMIN_ONLY)
  officeHoursHeld: Int! @visibility(class: ADMIN_ONLY)
  classesEnrolled: Int! @visibility(class: ADMIN_ONLY)
  classesCompleted: Int! @visibility(class: ADMIN_ONLY)
  computedAt: DateTime! @visibility(class: ADMIN_ONLY)
  provenance: Provenance! @visibility(class: ADMIN_ONLY)
}
TypeAdminNote

Olivier's notes; never visible to the company (REQ-CO-05).

"Olivier's notes; never visible to the company (REQ-CO-05)."
type AdminNote {
  id: ID! @visibility(class: ADMIN_ONLY)
  body: String! @visibility(class: ADMIN_ONLY)
  authorDisplayName: String! @visibility(class: ADMIN_ONLY) @personal @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  createdAt: DateTime! @visibility(class: ADMIN_ONLY)
}
TypeAdminFlag
type AdminFlag {
  id: ID! @visibility(class: ADMIN_ONLY)
  kind: AdminFlagKind! @visibility(class: ADMIN_ONLY)
  reason: String @visibility(class: ADMIN_ONLY)
  setAt: DateTime! @visibility(class: ADMIN_ONLY)
  clearedAt: DateTime @visibility(class: ADMIN_ONLY)
}
TypeIntroRequest

Admin + founder view of an intro request. Founders never see Olivier-declined requests.

"Admin + founder view of an intro request. Founders never see Olivier-declined requests."
type IntroRequest {
  id: ID! @visibility(class: ADMIN_ONLY)
  state: IntroRequestState! @visibility(class: ADMIN_ONLY)
  "The company the request is about; the intro queue lists it (REQ-CNS-04)."
  companyId: ID! @visibility(class: ADMIN_ONLY)
  companyName: String! @visibility(class: ADMIN_ONLY)
  investorDisplayName: String! @visibility(class: ADMIN_ONLY) @personal @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER, COMPANY_ADMIN])
  investorFirmName: String! @visibility(class: ADMIN_ONLY)
  reason: String! @visibility(class: ADMIN_ONLY)
  contextChips: [String!]! @visibility(class: ADMIN_ONLY)
  requestedAt: DateTime! @visibility(class: ADMIN_ONLY)
  passedToFounderAt: DateTime @visibility(class: ADMIN_ONLY)
  founderDecidedAt: DateTime @visibility(class: ADMIN_ONLY)
  introducedAt: DateTime @visibility(class: ADMIN_ONLY)
  "Visible to Olivier only once given; the founder sees their own."
  founderDeclineReason: String @visibility(class: ADMIN_ONLY)
  "Olivier's internal hold note; never to the company."
  olivierNote: String @visibility(class: ADMIN_ONLY)
}
TypeIntroRequestConnection
type IntroRequestConnection {
  edges: [IntroRequestEdge!]!
  pageInfo: PageInfo!
}
TypeIntroRequestEdge
type IntroRequestEdge {
  cursor: String!
  node: IntroRequest!
}
TypeDisclosureGrant

Append-only ledger row (REQ-CNS-01).

"Append-only ledger row (REQ-CNS-01)."
type DisclosureGrant {
  id: ID! @visibility(class: ADMIN_ONLY)
  scope: GrantScope! @visibility(class: ADMIN_ONLY)
  action: DisclosureGrantAction! @visibility(class: ADMIN_ONLY)
  audience: GrantAudience! @visibility(class: ADMIN_ONLY)
  allowlistFirmIds: [ID!]! @visibility(class: ADMIN_ONLY)
  denylistFirmIds: [ID!]! @visibility(class: ADMIN_ONLY)
  termsVersionHash: String! @visibility(class: ADMIN_ONLY)
  createdAt: DateTime! @visibility(class: ADMIN_ONLY)
}
TypeFounderBlock
type FounderBlock {
  id: ID! @visibility(class: ADMIN_ONLY)
  firmName: String! @visibility(class: ADMIN_ONLY)
  createdAt: DateTime! @visibility(class: ADMIN_ONLY)
}
TypeInvestorInterest

REQ-FND-13: null below the k-threshold. No names, no timestamps, ever.

"REQ-FND-13: null below the k-threshold. No names, no timestamps, ever."
type InvestorInterest {
  viewedBy30d: Int
  following: Int
}
TypeInvestorFirmRef

An admitted firm, as a founder sees it to decide whether to block it (REQ-FND-12/13).

"An admitted firm, as a founder sees it to decide whether to block it (REQ-FND-12/13)."
type InvestorFirmRef {
  id: ID!
  name: String!
}
TypeInvestorActivity

Per-company, paginated, confidentiality-labelled admin view (REQ-CNS-05, REQ-VC-15).

"Per-company, paginated, confidentiality-labelled admin view (REQ-CNS-05, REQ-VC-15)."
type InvestorActivity {
  occurredAt: DateTime!
  investorDisplayName: String! @personal @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  firmName: String!
  kind: String!
  resource: String!
}
TypeInvestorActivityConnection
type InvestorActivityConnection {
  edges: [InvestorActivityEdge!]!
  pageInfo: PageInfo!
  confidentialityLabel: String!
}
TypeInvestorActivityEdge
type InvestorActivityEdge {
  cursor: String!
  node: InvestorActivity!
}
TypeCohortDashboardRow

Admin cohort dashboard row (REQ-PRG-03). Facts only; no ranking.

"Admin cohort dashboard row (REQ-PRG-03). Facts only; no ranking."
type CohortDashboardRow {
  company: Company!
  latestUpdatePublishedAt: DateTime
  "\"updates: N of last M cycles\" as a fact."
  updatesInLastCycles: Int!
  cyclesConsidered: Int!
  overdue: Boolean!
  openIntroRequests: Int!
}
TypeIntakeMoveImpact

Shown before moving a company between intakes (REQ-CO-03).

"Shown before moving a company between intakes (REQ-CO-03)."
type IntakeMoveImpact {
  investorsLosingAccess: Int!
  investorsGainingAccess: Int!
}
TypeApplication
type Application {
  id: ID!
  outcome: ApplicationOutcome!
  companyName: String!
  "Self (the applicant) or role-authorised."
  applicantDisplayName: String! @personal @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, PROGRAMME_MANAGER, ATTENDEE, FOUNDER, COMPANY_ADMIN])
  occurrenceId: ID
  hasDeck: Boolean!
  sharedWithOlivier: Boolean!
  sharedWithInvestorAttendees: Boolean!
  createdAt: DateTime!
  decidedAt: DateTime
}
TypeDeckUploadTicket
type DeckUploadTicket {
  pitchMaterialId: ID!
  uploadUrl: URL!
  expiresAt: DateTime!
  "Exact honesty copy shown at upload (REQ-FND-08)."
  watermarkNotice: String!
}
TypeFounderPublishPreflight

Who will see what when this draft is published (REQ-FND-03, UX-91). Audience sizes, not interest.

"Who will see what when this draft is published (REQ-FND-03, UX-91). Audience sizes, not interest."
type FounderPublishPreflight {
  "Investors who would see the update on their next request (canSee at preflight time)."
  investorCount: Int!
  "Intakes the company is in (investor access is scoped to intakes)."
  intakeNames: [String!]!
  fields: [PreflightFieldAudience!]!
}
EnumPreflightAudience
enum PreflightAudience {
  INVESTORS
  OLIVIER
}
TypePreflightFieldAudience
type PreflightFieldAudience {
  field: String!
  label: String!
  audience: PreflightAudience!
  "Always Olivier-only; no switch exists (notes and asks for Olivier)."
  always: Boolean!
}
TypeCompanyVocabulary

Olivier's vocabularies a founder picks from (REQ-CO-04, REQ-ADM-18).

"Olivier's vocabularies a founder picks from (REQ-CO-04, REQ-ADM-18)."
type CompanyVocabulary {
  sectors: [String!]!
  stages: [String!]!
}
InputCompanyFilter
input CompanyFilter {
  intakeIds: [ID!]
  statuses: [CompanyStatus!]
  tiers: [ProgrammeTier!]
  "No update in N cycles (REQ-CO-05)."
  noUpdateInCycles: Int
  search: String
}
InputCreateCompanyInput
input CreateCompanyInput {
  name: String!
  intakeId: ID
  status: CompanyStatus = PROSPECT
  programmeTier: ProgrammeTier = COMMUNITY
}
InputCompanyAdminPatch

Olivier edits vocabulary fields only; never founder prose (REQ-CO-06).

"Olivier edits vocabulary fields only; never founder prose (REQ-CO-06)."
input CompanyAdminPatch {
  status: CompanyStatus
  programmeTier: ProgrammeTier
  stage: String
  sector: String
}
InputCompanyProfileInput
input CompanyProfileInput {
  oneLiner: String
  description: String
  sector: String
  stage: String
  website: URL
  location: String
  foundedYear: Int
}
InputTeamEntryInput
input TeamEntryInput {
  id: ID
  displayName: String!
  role: String
  oneLine: String
  linkedinUrl: URL
  position: Int
}
InputProgressDraftInput
input ProgressDraftInput {
  period: LocalDate!
  title: String
  whatHappened: String
  whatsNext: String
  lowlights: String
  asks: String
  asksOfOlivier: String
  notesForOlivier: String
  kpiValues: [KpiValueInput!]
  sectionVisibility: SectionVisibilityInput
  "Last updatedAt seen; stale saves are rejected (STALE_VERSION)."
  expectedUpdatedAt: DateTime
}
InputSectionVisibilityInput
input SectionVisibilityInput {
  whatHappened: VisibilityClass
  whatsNext: VisibilityClass
  lowlights: VisibilityClass
  asks: VisibilityClass
  kpis: VisibilityClass
}
InputKpiValueInput
input KpiValueInput {
  kpiDefinitionId: ID!
  value: String
  unit: String
  asOf: LocalDate!
}
InputKpiDefinitionInput
input KpiDefinitionInput {
  name: String!
  unit: String
  standardKey: String
  position: Int
}
InputMilestoneInput
input MilestoneInput {
  id: ID
  title: String!
  status: MilestoneStatus!
  dueDate: LocalDate
  completedOn: LocalDate
}
InputDeckUploadInput
input DeckUploadInput {
  filename: String!
  sizeBytes: Int!
  "Must be application/pdf (SEC-50)."
  contentType: String!
  downloadAllowed: Boolean = false
}
InputDisclosureGrantInput
input DisclosureGrantInput {
  companyId: ID!
  scope: GrantScope!
  audience: GrantAudience!
  allowlistFirmIds: [ID!]
  denylistFirmIds: [ID!]
  "Hash of the disclosure text the founder accepted."
  termsVersionHash: String!
}
InputApplicationInput
input ApplicationInput {
  occurrenceId: ID!
  companyId: ID
  "Creates a PROSPECT company when the applicant has none (REQ-ADM-07)."
  prospectCompanyName: String
  pitchMaterialId: ID
  "Two separate, unticked consents (REQ-ADM-07)."
  shareWithOlivier: Boolean!
  shareWithInvestorAttendees: Boolean!
  consentTextVersionHash: String!
}
TypeInvestorCompanyCard

Directory card: exactly these fields (REQ-VC-06, QA-VC-61 snapshot).

"Directory card: exactly these fields (REQ-VC-06, QA-VC-61 snapshot)."
type InvestorCompanyCard {
  id: ID! @visibility(class: PUBLIC, scope: PROFILE)
  name: String! @visibility(class: PUBLIC, scope: PROFILE)
  logoUrl: URL @visibility(class: PUBLIC, scope: PROFILE)
  oneLiner: String @visibility(class: PUBLIC, scope: PROFILE)
  sector: String @visibility(class: PUBLIC, scope: PROFILE)
  stage: String @visibility(class: INVESTOR, scope: PROFILE)
  intakeName: String @visibility(class: INVESTOR, scope: PROFILE)
  status: CompanyStatus @visibility(class: INVESTOR, scope: PROFILE)
  lastUpdatePublishedAt: DateTime @visibility(class: INVESTOR, scope: UPDATES)
  following: Boolean! @visibility(class: INVESTOR, scope: PROFILE)
}
TypeInvestorCompanyCardConnection
type InvestorCompanyCardConnection {
  edges: [InvestorCompanyCardEdge!]!
  pageInfo: PageInfo!
}
TypeInvestorCompanyCardEdge
type InvestorCompanyCardEdge {
  cursor: String!
  node: InvestorCompanyCard!
}
TypeInvestorCompany

Company page: header → updates → materials → team. No engagement in v1 (REQ-VC-07).

"Company page: header → updates → materials → team. No engagement in v1 (REQ-VC-07)."
type InvestorCompany {
  id: ID! @visibility(class: PUBLIC, scope: PROFILE)
  name: String! @visibility(class: PUBLIC, scope: PROFILE)
  logoUrl: URL @visibility(class: PUBLIC, scope: PROFILE)
  oneLiner: String @visibility(class: PUBLIC, scope: PROFILE)
  sector: String @visibility(class: PUBLIC, scope: PROFILE)
  description: String @visibility(class: INVESTOR, scope: PROFILE)
  stage: String @visibility(class: INVESTOR, scope: PROFILE)
  website: URL @visibility(class: INVESTOR, scope: PROFILE)
  location: String @visibility(class: INVESTOR, scope: PROFILE)
  foundedYear: Int @visibility(class: INVESTOR, scope: PROFILE)
  intakeName: String @visibility(class: INVESTOR, scope: PROFILE)
  status: CompanyStatus @visibility(class: INVESTOR, scope: PROFILE)
  "Own grant scope + allowlist only (REQ-VC-08, SEC-48)."
  fundraisingStatus: FundraisingStatus @visibility(class: INVESTOR, scope: FUNDRAISING_STATUS)
  lastUpdatePublishedAt: DateTime @visibility(class: INVESTOR, scope: UPDATES)
  "Current versions only; one dot per update on the client, no score (REQ-VC-07)."
  updates(first: Int = 10, after: String): InvestorUpdateConnection! @visibility(class: INVESTOR, scope: UPDATES)
  team: [InvestorTeamEntry!]! @visibility(class: INVESTOR, scope: FOUNDER_BIOS)
  decks: [InvestorDeck!]! @visibility(class: INVESTOR, scope: DECK)
  following: Boolean! @visibility(class: INVESTOR, scope: PROFILE)
  introStatus: InvestorIntroStatus! @visibility(class: INVESTOR, scope: PROFILE)
  canRequestIntro: Boolean! @visibility(class: INVESTOR, scope: PROFILE)
  "The investor's own intro state with dates; never another investor's (REQ-VC-11, SEC-49)."
  introState: InvestorIntroState! @visibility(class: INVESTOR, scope: PROFILE)
}
TypeInvestorUpdate

Only sections the founder made investor-visible are non-null; restricted ones are absent.

"Only sections the founder made investor-visible are non-null; restricted ones are absent."
type InvestorUpdate {
  id: ID! @visibility(class: INVESTOR, scope: UPDATES)
  period: LocalDate! @visibility(class: INVESTOR, scope: UPDATES)
  title: String! @visibility(class: INVESTOR, scope: UPDATES)
  publishedAt: DateTime! @visibility(class: INVESTOR, scope: UPDATES)
  editedAt: DateTime @visibility(class: INVESTOR, scope: UPDATES)
  whatHappened: String @visibility(class: INVESTOR, scope: UPDATES)
  whatsNext: String @visibility(class: INVESTOR, scope: UPDATES)
  lowlights: String @visibility(class: INVESTOR, scope: UPDATES)
  asks: String @visibility(class: INVESTOR, scope: UPDATES)
  "Plain table of founder-entered values; no computed math (REQ-VC-07)."
  kpis: [InvestorKpiValue!]! @visibility(class: INVESTOR, scope: KPIS)
  milestones: [InvestorMilestone!]! @visibility(class: INVESTOR, scope: UPDATES)
  provenance: Provenance! @visibility(class: INVESTOR, scope: UPDATES)
}
TypeInvestorUpdateConnection
type InvestorUpdateConnection {
  edges: [InvestorUpdateEdge!]!
  pageInfo: PageInfo!
}
TypeInvestorUpdateEdge
type InvestorUpdateEdge {
  cursor: String!
  node: InvestorUpdate!
}
TypeInvestorKpiValue
type InvestorKpiValue {
  name: String! @visibility(class: INVESTOR, scope: KPIS)
  value: String @visibility(class: INVESTOR, scope: KPIS)
  unit: String @visibility(class: INVESTOR, scope: KPIS)
  asOf: LocalDate! @visibility(class: INVESTOR, scope: KPIS)
  source: KpiSource! @visibility(class: INVESTOR, scope: KPIS)
}
TypeInvestorMilestone
type InvestorMilestone {
  title: String! @visibility(class: INVESTOR, scope: UPDATES)
  status: MilestoneStatus! @visibility(class: INVESTOR, scope: UPDATES)
  dueDate: LocalDate @visibility(class: INVESTOR, scope: UPDATES)
  completedOn: LocalDate @visibility(class: INVESTOR, scope: UPDATES)
}
TypeInvestorTeamEntry

Name, role, one line, optional LinkedIn — never contact details (SEC-38).

"Name, role, one line, optional LinkedIn — never contact details (SEC-38)."
type InvestorTeamEntry {
  displayName: String! @visibility(class: INVESTOR, scope: FOUNDER_BIOS)
  role: String @visibility(class: INVESTOR, scope: FOUNDER_BIOS)
  oneLine: String @visibility(class: INVESTOR, scope: FOUNDER_BIOS)
  linkedinUrl: URL @visibility(class: INVESTOR, scope: FOUNDER_BIOS)
}
TypeInvestorDeck
type InvestorDeck {
  id: ID! @visibility(class: INVESTOR, scope: DECK)
  version: Int! @visibility(class: INVESTOR, scope: DECK)
  pageCount: Int @visibility(class: INVESTOR, scope: DECK)
  uploadedAt: DateTime! @visibility(class: INVESTOR, scope: DECK)
  downloadAllowed: Boolean! @visibility(class: INVESTOR, scope: DECK)
  "One watermarked page; every view audited (REQ-VC-09)."
  page(number: Int!): InvestorDeckPage @visibility(class: INVESTOR, scope: DECK)
}
TypeInvestorDeckPage
type InvestorDeckPage {
  number: Int! @visibility(class: INVESTOR, scope: DECK)
  "≤5-min authenticated URL; re-rendered per zoom level with visible + forensic watermark."
  imageUrl: URL! @visibility(class: INVESTOR, scope: DECK)
  expiresAt: DateTime! @visibility(class: INVESTOR, scope: DECK)
  "Extracted text for screen readers (NFR accessibility)."
  altText: String @visibility(class: INVESTOR, scope: DECK)
}
TypeInvestorHome

"Since you last visited" (REQ-VC-05).

"\"Since you last visited\" (REQ-VC-05)."
type InvestorHome {
  "Literal marker shown to the investor; null on first visit (lands on Directory)."
  lastVisitedAt: DateTime
  pinned: [InvestorCompanyCard!]!
  updates(first: Int = 10, after: String): InvestorFeedConnection!
}
TypeInvestorFeedItem
type InvestorFeedItem {
  company: InvestorCompanyCard!
  update: InvestorUpdate!
}
TypeInvestorFeedConnection
type InvestorFeedConnection {
  edges: [InvestorFeedEdge!]!
  pageInfo: PageInfo!
}
TypeInvestorFeedEdge
type InvestorFeedEdge {
  cursor: String!
  node: InvestorFeedItem!
}
TypeInvestorMe

The investor's own account. No other investor's identity, follows or views (SEC-49).

"The investor's own account. No other investor's identity, follows or views (SEC-49)."
type InvestorMe {
  id: ID!
  displayName: String! @personal(self: true) @auth(roles: [INVESTOR])
  firmName: String!
  isOrgAdmin: Boolean!
  termsAccepted: Boolean!
  digestOptIn: Boolean!
  sessionExpiresAt: DateTime!
  "Olivier's reciprocal by-name follower policy, disclosed at the Terms gate when on (REQ-FND-13)."
  byNameFollowerPolicy: Boolean!
}
TypeTermsOfAccess

Olivier-authored Terms of Access (REQ-VC-03).

"Olivier-authored Terms of Access (REQ-VC-03)."
type TermsOfAccess {
  versionHash: String!
  title: String!
  body: String!
  effectiveAt: DateTime!
  "Olivier's reciprocal by-name follower policy, disclosed before acceptance (REQ-FND-13, REQ-CNS-06)."
  byNameFollowerPolicy: Boolean!
}
TypeInvestorIntroState
type InvestorIntroState {
  companyId: ID!
  status: InvestorIntroStatus!
  "Earliest re-request after a close (60 days, REQ-VC-11)."
  canRequestAgainAt: DateTime
  "When this investor's latest request was made; null if never (state dates on the page, T7)."
  requestedAt: DateTime
  "Set only once Olivier confirmed the off-platform introduction (REQ-VC-12)."
  introducedAt: DateTime
}
TypeDeckDownload
type DeckDownload {
  "Per-viewer watermarked PDF; hash logged; founder notified (REQ-VC-09, SEC-44)."
  url: URL!
  expiresAt: DateTime!
}
TypeInvestorDirectoryFacets

Directory filter vocabularies for this investor: labels only, never counts (REQ-VC-06, UX-68). Every value derives ONLY from the companies this investor currently holds an active grant for (the same set investorDirectory returns): a sector, stage or intake that exists only on ungranted, withdrawn or firm-blocked companies must not appear, or the facet list itself would leak existence (SEC-36).

"""
Directory filter vocabularies for this investor: labels only, never counts (REQ-VC-06, UX-68).
Every value derives ONLY from the companies this investor currently holds an active grant for
(the same set investorDirectory returns): a sector, stage or intake that exists only on
ungranted, withdrawn or firm-blocked companies must not appear, or the facet list itself would
leak existence (SEC-36).
"""
type InvestorDirectoryFacets {
  sectors: [String!]!
  stages: [String!]!
  "Intakes of granted companies only; cohort scoping, not a list endpoint (§11)."
  intakes: [InvestorIntakeOption!]!
}
TypeInvestorIntakeOption
type InvestorIntakeOption {
  id: ID!
  name: String!
}
InputInvestorDirectoryFilter
input InvestorDirectoryFilter {
  intakeIds: [ID!]
  sectors: [String!]
  stages: [String!]
  followedOnly: Boolean
  updatedWithinDays: Int
  "Name and one-liner only."
  search: String
}
InputIntroRequestInput
input IntroRequestInput {
  companyId: ID!
  "≤ 500 characters (REQ-VC-11)."
  reason: String!
  contextChips: [String!]
  "\"Olivier may share my name and firm with the founder\" — must be true."
  identityConsent: Boolean!
}
TypeInvestorOrg
type InvestorOrg {
  id: ID!
  name: String!
  domainAllowlist: [String!]!
  seatCap: Int!
  scopeSectors: [String!]!
  scopeStages: [String!]!
  scopeIntakeIds: [ID!]!
  status: InvestorOrgStatus!
  admittedAt: DateTime!
  members: [InvestorMemberAdmin!]!
}
TypeInvestorOrgConnection
type InvestorOrgConnection {
  edges: [InvestorOrgEdge!]!
  pageInfo: PageInfo!
}
TypeInvestorOrgEdge
type InvestorOrgEdge {
  cursor: String!
  node: InvestorOrg!
}
TypeInvestorMemberAdmin
type InvestorMemberAdmin {
  id: ID!
  displayName: String! @personal @auth(roles: [OWNER, ADMIN])
  email: String! @pii @auth(roles: [OWNER, ADMIN])
  status: InvestorMemberStatus!
  isOrgAdmin: Boolean!
  mfaEnrolled: Boolean!
  termsAccepted: Boolean!
  lastAccessAt: DateTime
  invitationExpiresAt: DateTime!
}
TypeInvestorAccessReviewRow

Quarterly access review row (REQ-VC-17).

"Quarterly access review row (REQ-VC-17)."
type InvestorAccessReviewRow {
  member: InvestorMemberAdmin!
  firmName: String!
  lastSeenAt: DateTime
  intakeNames: [String!]!
  follows: Int!
  openIntroRequests: Int!
  companyPagesViewed30d: Int!
  inactiveDays: Int
  recertifiedAt: DateTime
}
TypeInvestorAccessAttestation

Facts for the quarterly attestation (REQ-CNS-07, SEC-58). Three calendar months from `periodStart` in the organizer zone; `periodEnd` is exclusive. Numbers, never names.

"""
Facts for the quarterly attestation (REQ-CNS-07, SEC-58). Three calendar months from
`periodStart` in the organizer zone; `periodEnd` is exclusive. Numbers, never names.
"""
type InvestorAccessAttestation {
  periodStart: LocalDate!
  periodEnd: LocalDate!
  activeFirms: Int!
  activeInvestors: Int!
  firmsAdmitted: Int!
  invitationsIssued: Int!
  suspensions: Int!
  revocations: Int!
  firmRevocations: Int!
  softLocks: Int!
  recertified: Int!
  "Active investors with no access in the last 60 days at period end (REQ-VC-17)."
  inactive60d: Int!
  currentTermsVersionHash: String
  "Active investors who accepted the current Terms version (SEC-41 coverage)."
  activeOnCurrentTerms: Int!
  "Distinct (investor, company) pairs read in the period."
  companyReadPairs: Int!
  generatedAt: DateTime!
}
InputAdmitInvestorOrgInput
input AdmitInvestorOrgInput {
  name: String!
  domainAllowlist: [String!]!
  seatCap: Int!
  scopeSectors: [String!]
  scopeStages: [String!]
  scopeIntakeIds: [ID!]
}
InputInviteInvestorInput
input InviteInvestorInput {
  orgId: ID!
  name: String!
  email: EmailAddress!
  "Required for consumer domains (REQ-VC-01); audited."
  consumerDomainOverrideReason: String
  isOrgAdmin: Boolean = false
}
InputTermsOfAccessInput
input TermsOfAccessInput {
  title: String!
  body: String!
  effectiveAt: DateTime!
}
TypeStripeConnectStatus
type StripeConnectStatus {
  state: String!
  mode: String!
  accountId: String
  chargesEnabled: Boolean!
  payoutsEnabled: Boolean!
  detailsSubmitted: Boolean!
  checkedAt: DateTime
}
EnumMessagingProvider
enum MessagingProvider {
  TWILIO
  SENDGRID
}
EnumSendGridRegion
enum SendGridRegion {
  GLOBAL
  EU
}
TypeMessagingConnectionStatus
type MessagingConnectionStatus {
  provider: MessagingProvider!
  state: String!
  accountHint: String
  verifiedAt: DateTime
  checkedAt: DateTime
  deliveryEnabled: Boolean!
}
InputTwilioConnectionInput
input TwilioConnectionInput {
  accountSid: String!
  messagingServiceSid: String!
  apiKeySid: String
  secret: String!
}
InputSendGridConnectionInput
input SendGridConnectionInput {
  apiKey: String!
  region: SendGridRegion!
}
TypeQuery
type Query {
  messagingConnections: [MessagingConnectionStatus!]! @auth(roles: [OWNER]) @rateLimit(bucket: "messaging.status")
  stripeConnectStatus: StripeConnectStatus! @auth(roles: [OWNER]) @rateLimit(bucket: "stripe.status")
  organizer: PublicOrganizer! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
  "PUBLIC events only, in range (REQ-ATT-01)."
  publicEvents(range: DateRangeInput!, first: Int = 50, after: String): PublicOccurrenceConnection! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR])
  "null ⇒ 404 (INVITE_ONLY, INVESTOR-to-outsiders, drafts). COMMUNITY to non-members ⇒ gated."
  publicEvent(occurrenceId: ID!): PublicEventPage @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR])
  publicClass(seriesId: ID!): PublicEventPage @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR])
  "Tiers an access code unlocks for this event (T9). Unknown code ⇒ empty list, never an error."
  unlockTicketTiers(seriesId: ID!, code: String!): [PublicTicketTier!]! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @rateLimit(bucket: "checkout.code")
  "The caller's own passkeys and TOTP factors."
  myAuthenticators: [Authenticator!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
  "The caller's own live sessions."
  mySessions: [SessionSummary!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
  me: Me @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  "The caller's own session: principal kind, roles, MFA state. Lets the web mount role-gated controls only for roles that hold them (absent, not hidden); the server still gates every field (T3 resolves)."
  session: SessionInfo! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
  myRegistrations(first: Int = 20, after: String, includePast: Boolean = false): MyRegistrationConnection! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  myRegistration(id: ID!): MyRegistration @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  "Own confirmed admission tickets, one per class session."
  myCheckInTickets(registrationId: ID!): [CheckInTicket!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  officeHoursSlots(range: DateRangeInput!): [BookableSlot!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  myConsents: [ConsentRecord!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  "Own ICS subscription feeds (SEC-16). Never for investors (REQ-EML-09)."
  myCalendarFeeds: [CalendarFeed!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER])
  myCompanies: [Company!]! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  "Static cohort directory, only if Olivier enables it (REQ-FND-15)."
  cohortDirectory: [CohortDirectoryEntry!]! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  "Byte-identical to a real investor's response, through canSee (REQ-FND-05)."
  previewAsInvestor(companyId: ID!): InvestorCompany @auth(roles: [FOUNDER, COMPANY_ADMIN])
  "Admitted firms a founder may block (REQ-FND-12)."
  admittedInvestorFirms(companyId: ID!): [InvestorFirmRef!]! @auth(roles: [COMPANY_ADMIN])
  "Publish preflight: audience size, intakes and per-field audiences (REQ-FND-03)."
  founderPublishPreflight(draftId: ID!): FounderPublishPreflight! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  "Olivier's sector/stage vocabulary for the profile form (REQ-CO-04)."
  companyVocabulary: CompanyVocabulary! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  "Current consent wordings with their hashes (REQ-CNS-03, SEC-20)."
  consentTexts: [ConsentText!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  company(id: ID!): Company @auth(roles: [FOUNDER, COMPANY_ADMIN, PROGRAMME_MANAGER, ADMIN, OWNER])
  "Full calendar of all events, Agenda/Month (REQ-ADM-02). Range ≤ 13 months."
  calendar(range: DateRangeInput!, filter: CalendarFilter, first: Int = 500, after: String): OccurrenceConnection! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, PROGRAMME_MANAGER, VIEWER])
  series(id: ID!): EventSeries @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, PROGRAMME_MANAGER, VIEWER])
  occurrence(id: ID!): Occurrence @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, PROGRAMME_MANAGER, VIEWER, HOST])
  "Host's own assignments only; other occurrences ⇒ absent (REQ-HST-01)."
  hostAssignments(range: DateRangeInput!): [Occurrence!]! @auth(roles: [HOST])
  previewRecurrence(input: RecurrenceInput!): RecurrencePreview! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  publishPreflight(seriesId: ID!): PublishPreflight! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  "Opening a roster writes an audit row (REQ-ADM-11)."
  roster(occurrenceId: ID!, first: Int = 100, after: String): RosterConnection! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST]) @audited(action: "roster.view")
  classRoster(seriesId: ID!, first: Int = 100, after: String): RosterConnection! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST]) @audited(action: "roster.view")
  attendanceMatrix(seriesId: ID!): AttendanceMatrix! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
  approvalQueue(seriesId: ID!, first: Int = 50, after: String): [ApprovalItem!]! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  venues: [Venue!]! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  complianceReport(range: DateRangeInput!): [ComplianceMonth!]! @auth(roles: [OWNER, ADMIN])
  "Refunds that failed and need a human (T9, QA-STM-12). Retry with refundRegistration."
  refundsNeedingAction(first: Int = 50): [RefundIssue!]! @auth(roles: [OWNER, ADMIN])
  importJob(id: ID!): ImportJob @auth(roles: [OWNER, ADMIN])
  organizerSettings: OrganizerSettings! @auth(roles: [OWNER, ADMIN])
  staffMembers: [StaffMember!]! @auth(roles: [OWNER, ADMIN])
  auditLog(filter: AuditFilter, first: Int = 50, after: String): AuditEventConnection! @auth(roles: [OWNER, ADMIN]) @audited(action: "audit.view")
  companies(filter: CompanyFilter, first: Int = 50, after: String): CompanyConnection! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  intakes: [CohortIntake!]! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  cohortDashboard(intakeId: ID, tier: ProgrammeTier, overdueOnly: Boolean = false): [CohortDashboardRow!]! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  intakeMoveImpact(companyId: ID!, toIntakeId: ID!): IntakeMoveImpact! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  "Olivier's intro queue; no bulk actions (REQ-CNS-04)."
  introQueue(states: [IntroRequestState!], first: Int = 50, after: String): IntroRequestConnection! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  applications(seriesId: ID!, first: Int = 50, after: String): [Application!]! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, PROGRAMME_MANAGER])
  investorOrgs(first: Int = 20, after: String): InvestorOrgConnection! @auth(roles: [OWNER, ADMIN])
  "Per company, paginated (REQ-CNS-05, REQ-VC-15)."
  investorActivity(companyId: ID!, first: Int = 20, after: String): InvestorActivityConnection! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "investor_activity.view")
  investorAccessReview: [InvestorAccessReviewRow!]! @auth(roles: [OWNER])
  "Quarterly investor access attestation facts for Olivier (REQ-CNS-07, SEC-58). Counts only; signatures are out of band."
  investorAccessAttestation(quarterStart: LocalDate!): InvestorAccessAttestation! @auth(roles: [OWNER]) @audited(action: "investor.attestation")
  termsOfAccessVersions: [TermsOfAccess!]! @auth(roles: [OWNER, ADMIN])
  investorMe: InvestorMe! @auth(roles: [INVESTOR])
  currentTermsOfAccess: TermsOfAccess @auth(roles: [INVESTOR])
  investorHome: InvestorHome! @auth(roles: [INVESTOR]) @rateLimit(bucket: "investor.home")
  "Granted companies only — never a cohort list (REQ-VC-06). first ≤ 10 (SEC-43)."
  investorDirectory(filter: InvestorDirectoryFilter, first: Int = 10, after: String): InvestorCompanyCardConnection! @auth(roles: [INVESTOR]) @rateLimit(bucket: "investor.directory")
  "null ⇒ 404 without an active grant (SEC-36). Every read records a DisclosureAccessEvent."
  investorCompany(id: ID!): InvestorCompany @auth(roles: [INVESTOR]) @rateLimit(bucket: "investor.company_view") @audited(action: "investor.company.view")
  "Filter labels derived only from this investor's granted companies; no counts (REQ-VC-06)."
  investorDirectoryFacets: InvestorDirectoryFacets! @auth(roles: [INVESTOR]) @rateLimit(bucket: "investor.directory_facets") @audited(action: "investor.directory.facets")
  """
  One watermarked deck page, paged by the deck viewer without re-reading the company (REQ-VC-09).
  null ⇒ 404 without an active DECK grant. Own bucket so deck pages/minute is a separate signal
  (REQ-VC-18); every page view audited.
  """
  investorDeckPage(deckId: ID!, number: Int!): InvestorDeckPage @auth(roles: [INVESTOR]) @rateLimit(bucket: "investor.deck_page") @audited(action: "investor.deck.page_view")
  "INVESTOR-audience events (REQ-VC-14)."
  investorEvents(range: DateRangeInput!, first: Int = 10, after: String): PublicOccurrenceConnection! @auth(roles: [INVESTOR])
}
QuerymessagingConnections
messagingConnections: [MessagingConnectionStatus!]! @auth(roles: [OWNER]) @rateLimit(bucket: "messaging.status")
QuerystripeConnectStatus
stripeConnectStatus: StripeConnectStatus! @auth(roles: [OWNER]) @rateLimit(bucket: "stripe.status")
Queryorganizer
organizer: PublicOrganizer! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
QuerypublicEvents

PUBLIC events only, in range (REQ-ATT-01).

"PUBLIC events only, in range (REQ-ATT-01)."
publicEvents(range: DateRangeInput!, first: Int = 50, after: String): PublicOccurrenceConnection! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR])
QuerypublicEvent

null ⇒ 404 (INVITE_ONLY, INVESTOR-to-outsiders, drafts). COMMUNITY to non-members ⇒ gated.

"null ⇒ 404 (INVITE_ONLY, INVESTOR-to-outsiders, drafts). COMMUNITY to non-members ⇒ gated."
publicEvent(occurrenceId: ID!): PublicEventPage @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR])
QuerypublicClass
publicClass(seriesId: ID!): PublicEventPage @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR])
QueryunlockTicketTiers

Tiers an access code unlocks for this event (T9). Unknown code ⇒ empty list, never an error.

"Tiers an access code unlocks for this event (T9). Unknown code ⇒ empty list, never an error."
unlockTicketTiers(seriesId: ID!, code: String!): [PublicTicketTier!]! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @rateLimit(bucket: "checkout.code")
QuerymyAuthenticators

The caller's own passkeys and TOTP factors.

"The caller's own passkeys and TOTP factors."
myAuthenticators: [Authenticator!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
QuerymySessions

The caller's own live sessions.

"The caller's own live sessions."
mySessions: [SessionSummary!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
Queryme
me: Me @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
Querysession

The caller's own session: principal kind, roles, MFA state. Lets the web mount role-gated controls only for roles that hold them (absent, not hidden); the server still gates every field (T3 resolves).

"The caller's own session: principal kind, roles, MFA state. Lets the web mount role-gated controls only for roles that hold them (absent, not hidden); the server still gates every field (T3 resolves)."
session: SessionInfo! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
QuerymyRegistrations
myRegistrations(first: Int = 20, after: String, includePast: Boolean = false): MyRegistrationConnection! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
QuerymyRegistration
myRegistration(id: ID!): MyRegistration @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
QuerymyCheckInTickets

Own confirmed admission tickets, one per class session.

"Own confirmed admission tickets, one per class session."
myCheckInTickets(registrationId: ID!): [CheckInTicket!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
QueryofficeHoursSlots
officeHoursSlots(range: DateRangeInput!): [BookableSlot!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
QuerymyConsents
myConsents: [ConsentRecord!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
QuerymyCalendarFeeds

Own ICS subscription feeds (SEC-16). Never for investors (REQ-EML-09).

"Own ICS subscription feeds (SEC-16). Never for investors (REQ-EML-09)."
myCalendarFeeds: [CalendarFeed!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER])
QuerymyCompanies
myCompanies: [Company!]! @auth(roles: [FOUNDER, COMPANY_ADMIN])
QuerycohortDirectory

Static cohort directory, only if Olivier enables it (REQ-FND-15).

"Static cohort directory, only if Olivier enables it (REQ-FND-15)."
cohortDirectory: [CohortDirectoryEntry!]! @auth(roles: [FOUNDER, COMPANY_ADMIN])
QuerypreviewAsInvestor

Byte-identical to a real investor's response, through canSee (REQ-FND-05).

"Byte-identical to a real investor's response, through canSee (REQ-FND-05)."
previewAsInvestor(companyId: ID!): InvestorCompany @auth(roles: [FOUNDER, COMPANY_ADMIN])
QueryadmittedInvestorFirms

Admitted firms a founder may block (REQ-FND-12).

"Admitted firms a founder may block (REQ-FND-12)."
admittedInvestorFirms(companyId: ID!): [InvestorFirmRef!]! @auth(roles: [COMPANY_ADMIN])
QueryfounderPublishPreflight

Publish preflight: audience size, intakes and per-field audiences (REQ-FND-03).

"Publish preflight: audience size, intakes and per-field audiences (REQ-FND-03)."
founderPublishPreflight(draftId: ID!): FounderPublishPreflight! @auth(roles: [FOUNDER, COMPANY_ADMIN])
QuerycompanyVocabulary

Olivier's sector/stage vocabulary for the profile form (REQ-CO-04).

"Olivier's sector/stage vocabulary for the profile form (REQ-CO-04)."
companyVocabulary: CompanyVocabulary! @auth(roles: [FOUNDER, COMPANY_ADMIN])
QueryconsentTexts

Current consent wordings with their hashes (REQ-CNS-03, SEC-20).

"Current consent wordings with their hashes (REQ-CNS-03, SEC-20)."
consentTexts: [ConsentText!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
Querycompany
company(id: ID!): Company @auth(roles: [FOUNDER, COMPANY_ADMIN, PROGRAMME_MANAGER, ADMIN, OWNER])
Querycalendar

Full calendar of all events, Agenda/Month (REQ-ADM-02). Range ≤ 13 months.

"Full calendar of all events, Agenda/Month (REQ-ADM-02). Range ≤ 13 months."
calendar(range: DateRangeInput!, filter: CalendarFilter, first: Int = 500, after: String): OccurrenceConnection! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, PROGRAMME_MANAGER, VIEWER])
Queryseries
series(id: ID!): EventSeries @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, PROGRAMME_MANAGER, VIEWER])
Queryoccurrence
occurrence(id: ID!): Occurrence @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, PROGRAMME_MANAGER, VIEWER, HOST])
QueryhostAssignments

Host's own assignments only; other occurrences ⇒ absent (REQ-HST-01).

"Host's own assignments only; other occurrences ⇒ absent (REQ-HST-01)."
hostAssignments(range: DateRangeInput!): [Occurrence!]! @auth(roles: [HOST])
QuerypreviewRecurrence
previewRecurrence(input: RecurrenceInput!): RecurrencePreview! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
QuerypublishPreflight
publishPreflight(seriesId: ID!): PublishPreflight! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
Queryroster

Opening a roster writes an audit row (REQ-ADM-11).

"Opening a roster writes an audit row (REQ-ADM-11)."
roster(occurrenceId: ID!, first: Int = 100, after: String): RosterConnection! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST]) @audited(action: "roster.view")
QueryclassRoster
classRoster(seriesId: ID!, first: Int = 100, after: String): RosterConnection! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST]) @audited(action: "roster.view")
QueryattendanceMatrix
attendanceMatrix(seriesId: ID!): AttendanceMatrix! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
QueryapprovalQueue
approvalQueue(seriesId: ID!, first: Int = 50, after: String): [ApprovalItem!]! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
Queryvenues
venues: [Venue!]! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
QuerycomplianceReport
complianceReport(range: DateRangeInput!): [ComplianceMonth!]! @auth(roles: [OWNER, ADMIN])
QueryrefundsNeedingAction

Refunds that failed and need a human (T9, QA-STM-12). Retry with refundRegistration.

"Refunds that failed and need a human (T9, QA-STM-12). Retry with refundRegistration."
refundsNeedingAction(first: Int = 50): [RefundIssue!]! @auth(roles: [OWNER, ADMIN])
QueryimportJob
importJob(id: ID!): ImportJob @auth(roles: [OWNER, ADMIN])
QueryorganizerSettings
organizerSettings: OrganizerSettings! @auth(roles: [OWNER, ADMIN])
QuerystaffMembers
staffMembers: [StaffMember!]! @auth(roles: [OWNER, ADMIN])
QueryauditLog
auditLog(filter: AuditFilter, first: Int = 50, after: String): AuditEventConnection! @auth(roles: [OWNER, ADMIN]) @audited(action: "audit.view")
Querycompanies
companies(filter: CompanyFilter, first: Int = 50, after: String): CompanyConnection! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
Queryintakes
intakes: [CohortIntake!]! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
QuerycohortDashboard
cohortDashboard(intakeId: ID, tier: ProgrammeTier, overdueOnly: Boolean = false): [CohortDashboardRow!]! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
QueryintakeMoveImpact
intakeMoveImpact(companyId: ID!, toIntakeId: ID!): IntakeMoveImpact! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
QueryintroQueue

Olivier's intro queue; no bulk actions (REQ-CNS-04).

"Olivier's intro queue; no bulk actions (REQ-CNS-04)."
introQueue(states: [IntroRequestState!], first: Int = 50, after: String): IntroRequestConnection! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
Queryapplications
applications(seriesId: ID!, first: Int = 50, after: String): [Application!]! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, PROGRAMME_MANAGER])
QueryinvestorOrgs
investorOrgs(first: Int = 20, after: String): InvestorOrgConnection! @auth(roles: [OWNER, ADMIN])
QueryinvestorActivity

Per company, paginated (REQ-CNS-05, REQ-VC-15).

"Per company, paginated (REQ-CNS-05, REQ-VC-15)."
investorActivity(companyId: ID!, first: Int = 20, after: String): InvestorActivityConnection! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "investor_activity.view")
QueryinvestorAccessReview
investorAccessReview: [InvestorAccessReviewRow!]! @auth(roles: [OWNER])
QueryinvestorAccessAttestation

Quarterly investor access attestation facts for Olivier (REQ-CNS-07, SEC-58). Counts only; signatures are out of band.

"Quarterly investor access attestation facts for Olivier (REQ-CNS-07, SEC-58). Counts only; signatures are out of band."
investorAccessAttestation(quarterStart: LocalDate!): InvestorAccessAttestation! @auth(roles: [OWNER]) @audited(action: "investor.attestation")
QuerytermsOfAccessVersions
termsOfAccessVersions: [TermsOfAccess!]! @auth(roles: [OWNER, ADMIN])
QueryinvestorMe
investorMe: InvestorMe! @auth(roles: [INVESTOR])
QuerycurrentTermsOfAccess
currentTermsOfAccess: TermsOfAccess @auth(roles: [INVESTOR])
QueryinvestorHome
investorHome: InvestorHome! @auth(roles: [INVESTOR]) @rateLimit(bucket: "investor.home")
QueryinvestorDirectory

Granted companies only — never a cohort list (REQ-VC-06). first ≤ 10 (SEC-43).

"Granted companies only — never a cohort list (REQ-VC-06). first ≤ 10 (SEC-43)."
investorDirectory(filter: InvestorDirectoryFilter, first: Int = 10, after: String): InvestorCompanyCardConnection! @auth(roles: [INVESTOR]) @rateLimit(bucket: "investor.directory")
QueryinvestorCompany

null ⇒ 404 without an active grant (SEC-36). Every read records a DisclosureAccessEvent.

"null ⇒ 404 without an active grant (SEC-36). Every read records a DisclosureAccessEvent."
investorCompany(id: ID!): InvestorCompany @auth(roles: [INVESTOR]) @rateLimit(bucket: "investor.company_view") @audited(action: "investor.company.view")
QueryinvestorDirectoryFacets

Filter labels derived only from this investor's granted companies; no counts (REQ-VC-06).

"Filter labels derived only from this investor's granted companies; no counts (REQ-VC-06)."
investorDirectoryFacets: InvestorDirectoryFacets! @auth(roles: [INVESTOR]) @rateLimit(bucket: "investor.directory_facets") @audited(action: "investor.directory.facets")
QueryinvestorDeckPage

One watermarked deck page, paged by the deck viewer without re-reading the company (REQ-VC-09). null ⇒ 404 without an active DECK grant. Own bucket so deck pages/minute is a separate signal (REQ-VC-18); every page view audited.

"""
One watermarked deck page, paged by the deck viewer without re-reading the company (REQ-VC-09).
null ⇒ 404 without an active DECK grant. Own bucket so deck pages/minute is a separate signal
(REQ-VC-18); every page view audited.
"""
investorDeckPage(deckId: ID!, number: Int!): InvestorDeckPage @auth(roles: [INVESTOR]) @rateLimit(bucket: "investor.deck_page") @audited(action: "investor.deck.page_view")
QueryinvestorEvents

INVESTOR-audience events (REQ-VC-14).

"INVESTOR-audience events (REQ-VC-14)."
investorEvents(range: DateRangeInput!, first: Int = 10, after: String): PublicOccurrenceConnection! @auth(roles: [INVESTOR])
TypeMutation
type Mutation {
  recordComplianceActivity(input: ComplianceActivityInput!): ComplianceActivity! @auth(roles: [OWNER, ADMIN])
  voidComplianceActivity(id: ID!, reason: String!): Boolean! @auth(roles: [OWNER, ADMIN])
  connectTwilio(input: TwilioConnectionInput!): MessagingConnectionStatus! @auth(roles: [OWNER]) @stepUp @rateLimit(bucket: "messaging.manage")
  connectSendGrid(input: SendGridConnectionInput!): MessagingConnectionStatus! @auth(roles: [OWNER]) @stepUp @rateLimit(bucket: "messaging.manage")
  recheckMessagingConnection(provider: MessagingProvider!): MessagingConnectionStatus! @auth(roles: [OWNER]) @stepUp @rateLimit(bucket: "messaging.manage")
  removeMessagingConnection(provider: MessagingProvider!): MessagingConnectionStatus! @auth(roles: [OWNER]) @stepUp @rateLimit(bucket: "messaging.manage")
  startStripeConnect: StripeConnectLink! @auth(roles: [OWNER]) @stepUp @rateLimit(bucket: "stripe.connect")
  requestManageLink(email: EmailAddress!): Boolean! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN]) @rateLimit(bucket: "auth.link")
  requestStaffSignInLink(email: EmailAddress!): Boolean! @auth(roles: [ANON]) @rateLimit(bucket: "auth.link")
  "POST-consumes a single-use signed link; sets the session cookie (SEC-13)."
  exchangeSignedLink(token: String!): SessionInfo! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN]) @rateLimit(bucket: "auth.exchange")
  startPasskeyRegistration: WebAuthnChallenge! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
  finishPasskeyRegistration(challengeId: ID!, response: WebAuthnJSON!): SessionInfo! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
  startPasskeyAuthentication: WebAuthnChallenge! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
  finishPasskeyAuthentication(challengeId: ID!, response: WebAuthnJSON!): SessionInfo! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
  "Starts TOTP enrolment (fallback factor). Allowed only where passkey enrolment is (T3)."
  startTotpEnrolment: TotpEnrolment! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
  verifyTotp(code: String!): SessionInfo! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR]) @rateLimit(bucket: "auth.totp")
  "Owner recovery of another account's TOTP lock; preserves daily budget and never grants MFA."
  resetTotpLock(principalKind: PrincipalKind!, principalId: ID!): Boolean! @auth(roles: [OWNER]) @stepUp
  "Remove one of the caller's own factors; needs step-up; never the last factor of staff/investors."
  revokeAuthenticator(id: ID!): Boolean! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR]) @stepUp @audited(action: "auth.authenticator.revoke")
  "End one of the caller's own sessions."
  revokeSession(id: ID!): Boolean! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR]) @audited(action: "auth.session.revoke")
  "End only this browser session, including incomplete sign-in; safe when already signed out."
  signOut: Boolean! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
  register(input: RegisterInput!): RegistrationResult! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotent @rateLimit(bucket: "register")
  verifyRegistrationEmail(token: String!): MyRegistration! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN])
  "Idempotent (REQ-REG-04). GET renders a confirm page; this POST mutates (REQ-ATT-03)."
  cancelRegistration(registrationId: ID!, scope: CancelScope = THIS): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotent
  acceptWaitlistOffer(registrationId: ID!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotent
  rejoinWaitlist(registrationId: ID!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotent
  setClassSessionOptOut(registrationId: ID!, occurrenceId: ID!, optedOut: Boolean!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  "≥24 h elapsed lead (REQ-ATT-10)."
  bookOfficeHoursSlot(occurrenceId: ID!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN]) @idempotent
  "≥12 h elapsed notice (REQ-ATT-10)."
  cancelOfficeHoursBooking(registrationId: ID!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN]) @idempotent
  updateReminderPreferences(input: ReminderPreferencesInput!): ReminderPreferences! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  setAttendeeListOptIn(registrationId: ID!, optIn: Boolean!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR])
  "Mint a revocable per-device ICS feed token (SEC-16). Not for investors (REQ-EML-09)."
  createCalendarFeed(label: String!): CalendarFeedCreated! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER]) @audited(action: "calendar_feed.create") @rateLimit(bucket: "calendar_feed")
  revokeCalendarFeed(id: ID!): Boolean! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER]) @audited(action: "calendar_feed.revoke")
  recordConsent(input: ConsentInput!): ConsentRecord! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN]) @idempotent
  "Stripe Checkout on Olivier's connected account (T9, SEC-22)."
  startCheckout(registrationId: ID!, ticketTierId: ID, promoCode: String, useAccountCredit: Boolean = false): CheckoutSession! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotent @rateLimit(bucket: "checkout")
  updateCompanyProfile(companyId: ID!, input: CompanyProfileInput!): Company! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  upsertTeamEntry(companyId: ID!, input: TeamEntryInput!): CompanyTeamEntry! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  removeTeamEntry(id: ID!): Boolean! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  "Per-founder opt-in for their own entry (REQ-CNS-01)."
  setTeamEntryVisibility(id: ID!, visibility: VisibilityClass!): CompanyTeamEntry! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp
  "Autosave ≤ 5 s (REQ-FND-02)."
  saveProgressDraft(companyId: ID!, input: ProgressDraftInput!): ProgressUpdateDraft! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  discardProgressDraft(draftId: ID!): Boolean! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  publishProgressUpdate(draftId: ID!): ProgressUpdate! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp @idempotent @audited(action: "progress.publish")
  "New immutable version; investors see \"Edited <date>\" (REQ-FND-03)."
  amendProgressUpdate(updateId: ID!, input: ProgressDraftInput!): ProgressUpdate! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp @idempotent @audited(action: "progress.amend")
  defineKpi(companyId: ID!, input: KpiDefinitionInput!): KpiDefinition! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  archiveKpi(kpiDefinitionId: ID!): KpiDefinition! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  setKpiVisibility(kpiDefinitionId: ID!, visibility: VisibilityClass!): KpiDefinition! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp
  upsertMilestone(companyId: ID!, input: MilestoneInput!): Milestone! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  setMilestoneVisibility(milestoneId: ID!, visibility: VisibilityClass!): Milestone! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp
  setFundraisingStatus(companyId: ID!, status: FundraisingStatus): Company! @auth(roles: [COMPANY_ADMIN]) @stepUp
  setCompanyFieldVisibility(companyId: ID!, field: CompanyVisibilityField!, visibility: VisibilityClass!): Company! @auth(roles: [COMPANY_ADMIN]) @stepUp
  "Widening to investors needs step-up; turning off never does (REQ-FND-10)."
  setCompanyInvestorVisibility(companyId: ID!, visible: Boolean!): Company! @auth(roles: [COMPANY_ADMIN])
  requestDeckUpload(companyId: ID!, input: DeckUploadInput!): DeckUploadTicket! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp @rateLimit(bucket: "deck.upload")
  confirmDeckUpload(pitchMaterialId: ID!): PitchMaterial! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  setDeckOptions(pitchMaterialId: ID!, downloadAllowed: Boolean, visibility: VisibilityClass): PitchMaterial! @auth(roles: [COMPANY_ADMIN]) @stepUp
  "Append-only grant row (REQ-CNS-01, SEC-36)."
  grantDisclosure(input: DisclosureGrantInput!): DisclosureGrant! @auth(roles: [COMPANY_ADMIN]) @stepUp @idempotent @audited(action: "grant.grant")
  "Effective on the next request; no step-up needed to narrow (REQ-FND-10, SEC-52)."
  withdrawDisclosure(companyId: ID!, scope: GrantScope!): DisclosureGrant! @auth(roles: [COMPANY_ADMIN]) @idempotent @audited(action: "grant.withdraw")
  pauseInvestorVisibility(companyId: ID!): [DisclosureGrant!]! @auth(roles: [COMPANY_ADMIN]) @audited(action: "grant.pause")
  resumeInvestorVisibility(companyId: ID!): [DisclosureGrant!]! @auth(roles: [COMPANY_ADMIN]) @stepUp @audited(action: "grant.resume")
  blockInvestorFirm(companyId: ID!, investorOrgId: ID!): FounderBlock! @auth(roles: [COMPANY_ADMIN]) @audited(action: "founder_block.add")
  unblockInvestorFirm(founderBlockId: ID!): Boolean! @auth(roles: [COMPANY_ADMIN]) @stepUp @audited(action: "founder_block.remove")
  "A founder takes their own update off investor view (REQ-FND-03/10). Narrowing: no step-up. Amend republishes."
  unpublishOwnUpdate(updateId: ID!): ProgressUpdate! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @audited(action: "progress.founder_unpublish")
  "Lengthen (or restore) the reminder cadence; never below Olivier's minimum (REQ-PRG-05)."
  setUpdateCadence(companyId: ID!, months: Int!): UpdateCadence! @auth(roles: [COMPANY_ADMIN]) @audited(action: "company.cadence")
  "Snooze this cycle's reminder, one tap (REQ-FND-07). Idempotent."
  snoozeCadenceReminder(companyId: ID!): UpdateCadence! @auth(roles: [FOUNDER, COMPANY_ADMIN])
  "Accept / decline / ask Olivier to hold. Contact details are never revealed (REQ-FND-11)."
  respondToIntroRequest(introRequestId: ID!, decision: FounderIntroDecision!, declineReason: String): IntroRequest! @auth(roles: [COMPANY_ADMIN]) @audited(action: "intro.founder_decision")
  "Apply to pitch with the existing profile and deck (REQ-FND-14, REQ-ADM-07)."
  applyToPitch(input: ApplicationInput!): Application! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN]) @idempotent
  "DSAR-style JSON for the company's own data (SEC-24)."
  exportCompanyData(companyId: ID!): RosterExport! @auth(roles: [COMPANY_ADMIN]) @stepUp @audited(action: "company.export") @rateLimit(bucket: "export")
  createEventSeries(input: EventSeriesInput!): EventSeries! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER]) @idempotent
  "THIS / THIS_AND_FOLLOWING / ALL; 409 STALE_VERSION on a stale expectedVersion (REQ-ADM-10)."
  updateEventSeries(
    seriesId: ID!
    expectedVersion: Int!
    input: EventSeriesPatch!
    scope: EditScope = THIS
    fromOccurrenceId: ID
    notifyAttendees: Boolean = true
    "ALL temporal edits: also retime occurrences that have their own time (QA-EDT-06)."
    resetExceptions: Boolean = false
    "Plan only: return the impact without writing (shown before the admin confirms)."
    dryRun: Boolean = false
  ): EditResult! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  updateOccurrence(occurrenceId: ID!, expectedVersion: Int!, input: OccurrencePatch!, notifyAttendees: Boolean = true): EditResult! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  "Hosts edit description/agenda only (REQ-HST-03)."
  updateOccurrenceDescription(occurrenceId: ID!, expectedVersion: Int!, description: String!): Occurrence! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
  "Cancel the 10 s pending update notification (REQ-ADM-09)."
  undoPendingNotification(pendingNotificationId: ID!): Boolean! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  publishSeries(seriesId: ID!, expectedVersion: Int!): EventSeries! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  cloneSeries(seriesId: ID!): EventSeries! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER]) @idempotent
  "Cancel ≠ delete; past occurrences with attendance are never deleted (REQ-ADM-10)."
  cancelOccurrence(occurrenceId: ID!, reason: String, notifyAttendees: Boolean = true, settlements: [CancellationSettlementInput!]): Occurrence! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  cancelSeries(seriesId: ID!, scope: EditScope = ALL, fromOccurrenceId: ID, reason: String, settlements: [CancellationSettlementInput!]): EditResult! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  "Choose-who flow when decreasing below confirmed (REQ-CAP-04)."
  setCapacity(seriesId: ID, occurrenceId: ID, capacity: Int, releaseRegistrationIds: [ID!]): Occurrence @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  updateEventBrief(seriesId: ID!, input: EventBriefInput!): EventSeries! @auth(roles: [OWNER, ADMIN])
  upsertVenue(id: ID, input: VenueInput!): Venue! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  archiveVenue(id: ID!): Boolean! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  assignHost(seriesId: ID!, occurrenceId: ID, staffUserId: ID, guestDisplayName: String): Host! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  removeHost(hostId: ID!): Boolean! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  upsertRegistrationQuestion(seriesId: ID!, id: ID, label: String!, kind: String!, options: [String!], required: Boolean!): RegistrationQuestion! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  upsertTicketTier(id: ID, input: TicketTierInput!): TicketTier! @auth(roles: [OWNER, ADMIN])
  upsertPromoCode(id: ID, input: PromoCodeInput!): PromoCode! @auth(roles: [OWNER, ADMIN])
  "Attendee-cancellation refund policy shown on the event page (T9, REQ-STM-03)."
  setRefundPolicy(seriesId: ID!, input: RefundPolicyInput!): RefundPolicy! @auth(roles: [OWNER, ADMIN]) @audited(action: "payment.refund_policy")
  requestMaterialUpload(input: MaterialUploadInput!): UploadTicket! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
  confirmMaterialUpload(objectId: ID!): Material! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
  setRecording(occurrenceId: ID!, url: URL, access: MaterialAccessLevel): Occurrence! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
  "Obeys the capacity invariant (REQ-CAP-07)."
  addRegistrant(input: ManualRegistrationInput!): RosterEntry! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER]) @idempotent
  approveRegistration(registrationId: ID!): RosterEntry! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  declineRegistration(registrationId: ID!, message: String): RosterEntry! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  promoteFromWaitlist(registrationId: ID!): RosterEntry! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  cancelRegistrationAsOrganizer(registrationId: ID!, reason: String): RosterEntry! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  "Bulk attendance; flips allowed within 7 days (REQ-ADM-12, REQ-STM-02)."
  markAttendance(occurrenceId: ID!, marks: [AttendanceInput!]!): [RosterEntry!]! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
  closeAttendance(occurrenceId: ID!): Occurrence! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
  "Offline check-in queue, idempotent per clientId (REQ-ADM-20)."
  syncCheckIns(items: [CheckInSyncItem!]!): CheckInSyncResult! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST]) @idempotent
  "Reveal one registrant's email (masked by default); audited (REQ-ADM-11)."
  revealRegistrantEmail(registrationId: ID!): String! @pii @auth(roles: [OWNER, ADMIN, EVENT_MANAGER]) @audited(action: "roster.reveal_email") @rateLimit(bucket: "roster.reveal")
  "THE roster export: Owner only, step-up, rate-limited, audited, other Owners notified ≤ 5 min (REQ-ADM-13)."
  exportRoster(occurrenceId: ID!): RosterExport! @auth(roles: [OWNER]) @stepUp @audited(action: "roster.export") @rateLimit(bucket: "export")
  "Per-event transactional message, ≤ 1/event/hour, Owner-notified. No newsletters (REQ-ADM-19)."
  sendEventMessage(occurrenceId: ID!, subject: String!, body: String!): Boolean! @auth(roles: [OWNER, ADMIN]) @idempotent @audited(action: "event.message") @rateLimit(bucket: "event.message")
  decideApplication(applicationId: ID!, outcome: ApplicationOutcome!): Application! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, PROGRAMME_MANAGER])
  "Organizer cancellation → full refund regardless of window (REQ-STM-05)."
  refundRegistration(registrationId: ID!, reason: String!): PaymentSummary! @auth(roles: [OWNER, ADMIN]) @stepUp @idempotent @audited(action: "payment.refund")
  requestImportUpload(source: ImportSource!): UploadTicket! @auth(roles: [OWNER, ADMIN])
  "Dry run first; sends zero emails (REQ-MIG-02/07)."
  startImport(input: ImportInput!): ImportJob! @auth(roles: [OWNER, ADMIN]) @idempotent
  commitImport(importJobId: ID!): ImportJob! @auth(roles: [OWNER, ADMIN]) @idempotent @audited(action: "import.commit")
  "(T11) Reminders for an imported event's imported registrations: opt-in, default off (REQ-MIG-02, QA-MIG-12)."
  setImportedEventReminders(seriesId: ID!, enabled: Boolean!): Boolean! @auth(roles: [OWNER, ADMIN]) @audited(action: "import.reminders")
  updateOrganizerSettings(input: OrganizerSettingsInput!): OrganizerSettings! @auth(roles: [OWNER, ADMIN]) @audited(action: "settings.update")
  updateVisibilityPolicy(input: VisibilityPolicyInput!): VisibilityPolicy! @auth(roles: [OWNER]) @stepUp @audited(action: "visibility_policy.update")
  inviteStaff(email: EmailAddress!, displayName: String!, roles: [OrganizerRole!]!): StaffMember! @auth(roles: [OWNER]) @stepUp @audited(action: "staff.invite")
  grantRole(staffUserId: ID!, role: OrganizerRole!): StaffMember! @auth(roles: [OWNER]) @stepUp @audited(action: "role.grant")
  revokeRole(staffUserId: ID!, role: OrganizerRole!, reason: String!): StaffMember! @auth(roles: [OWNER]) @stepUp @audited(action: "role.revoke")
  "DSAR for one person (SEC-24); AF roles have no equivalent."
  exportMemberData(attendeeId: ID!): RosterExport! @auth(roles: [OWNER]) @stepUp @audited(action: "member.export") @rateLimit(bucket: "export")
  "Pseudonymise within 30 days; reminders stop immediately (REQ-ATT-11)."
  requestMemberErasure(attendeeId: ID!, reason: String!): Boolean! @auth(roles: [OWNER]) @stepUp @audited(action: "member.erasure")
  createCompany(input: CreateCompanyInput!): Company! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @idempotent
  updateCompanyAdminFields(companyId: ID!, input: CompanyAdminPatch!): Company! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "company.admin_update")
  promoteProspect(companyId: ID!): Company! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  inviteFounder(companyId: ID!, email: EmailAddress!, displayName: String!, role: CompanyMemberRole!): CompanyMember! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "company.invite_founder")
  removeCompanyMember(companyMemberId: ID!, reason: String!): CompanyMember! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "company.remove_member")
  "With a reason; all company members notified (REQ-CO-02, SEC-53)."
  reassignCompanyAdmin(companyId: ID!, companyMemberId: ID!, reason: String!): CompanyMember! @auth(roles: [OWNER]) @stepUp @audited(action: "company.reassign_admin")
  "Requires the IntakeMoveImpact preview to have been shown (REQ-CO-03)."
  moveCompanyIntake(companyId: ID!, toIntakeId: ID!, confirmedImpact: Boolean!): Company! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "company.move_intake")
  archiveCompany(companyId: ID!, reason: String!): Company! @auth(roles: [OWNER, ADMIN]) @audited(action: "company.archive")
  "Deletion needs founder + Owner dual approval (REQ-CO-06, SEC-53)."
  approveCompanyDeletion(companyId: ID!): Company! @auth(roles: [OWNER, COMPANY_ADMIN]) @stepUp @audited(action: "company.delete_approval")
  createIntake(name: String!, startsOn: LocalDate!, endsOn: LocalDate, accessGroups: [String!]): CohortIntake! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  upsertMilestoneTemplate(intakeId: ID!, id: ID, title: String!, position: Int!): Boolean! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  addAdminNote(companyId: ID!, body: String!): AdminNote! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  setAdminFlag(companyId: ID!, kind: AdminFlagKind!, reason: String): AdminFlag! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  clearAdminFlag(adminFlagId: ID!): AdminFlag! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  "Reason shown to the founder (REQ-CO-06)."
  unpublishProgressUpdate(updateId: ID!, reason: String!): ProgressUpdate! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "progress.unpublish")
  "Private note or structured clarification; ADMIN_ONLY (REQ-PRG-06)."
  requestUpdateClarification(updateId: ID!, note: String!): Boolean! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
  "Pass / hold / decline. No bulk actions (REQ-CNS-04)."
  actOnIntroRequest(introRequestId: ID!, action: OlivierIntroAction!, note: String): IntroRequest! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "intro.olivier_action")
  "Requires confirming the introduction email was sent off-platform (REQ-CNS-04, REQ-VC-12)."
  markIntroduced(introRequestId: ID!, introductionEmailSent: Boolean!): IntroRequest! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "intro.introduced")
  admitInvestorOrg(input: AdmitInvestorOrgInput!): InvestorOrg! @auth(roles: [OWNER]) @stepUp @audited(action: "investor_org.admit")
  "Firm work-email domain; 7-day invitation (REQ-VC-01)."
  inviteInvestor(input: InviteInvestorInput!): InvestorMemberAdmin! @auth(roles: [OWNER]) @stepUp @audited(action: "investor.invite")
  "Kills sessions within one request (REQ-VC-04)."
  suspendInvestor(memberId: ID!, reason: String!): InvestorMemberAdmin! @auth(roles: [OWNER]) @audited(action: "investor.suspend")
  revokeInvestor(memberId: ID!, reason: String!): InvestorMemberAdmin! @auth(roles: [OWNER]) @audited(action: "investor.revoke")
  "Owner unlock after an anomaly soft-lock, with reason (REQ-VC-18)."
  unlockInvestor(memberId: ID!, reason: String!): InvestorMemberAdmin! @auth(roles: [OWNER]) @stepUp @audited(action: "investor.unlock")
  recertifyInvestors(memberIds: [ID!]!): [InvestorMemberAdmin!]! @auth(roles: [OWNER]) @audited(action: "investor.recertify")
  "New version blocks investor access until re-accepted (SEC-41)."
  publishTermsOfAccess(input: TermsOfAccessInput!): TermsOfAccess! @auth(roles: [OWNER]) @stepUp @audited(action: "terms.publish")
  acceptTermsOfAccess(versionHash: String!): InvestorMe! @auth(roles: [INVESTOR]) @audited(action: "terms.accept")
  markInvestorHomeVisited: InvestorMe! @auth(roles: [INVESTOR])
  setDigestOptIn(optIn: Boolean!): InvestorMe! @auth(roles: [INVESTOR])
  followCompany(companyId: ID!): InvestorCompanyCard! @auth(roles: [INVESTOR])
  unfollowCompany(companyId: ID!): InvestorCompanyCard! @auth(roles: [INVESTOR])
  "One open request per (investor, company); monthly cap (REQ-VC-11)."
  requestIntro(input: IntroRequestInput!): InvestorIntroState! @auth(roles: [INVESTOR]) @idempotent @rateLimit(bucket: "investor.intro") @audited(action: "intro.request")
  withdrawIntroRequest(companyId: ID!): InvestorIntroState! @auth(roles: [INVESTOR])
  "Only if the founder enabled download; per-viewer watermarked PDF (REQ-VC-09)."
  requestDeckDownload(deckId: ID!): DeckDownload! @auth(roles: [INVESTOR]) @stepUp @audited(action: "deck.download") @rateLimit(bucket: "investor.deck_download")
  "Firm org admin manages seats within the cap; Olivier can override (REQ-VC-16)."
  removeInvestorSeat(memberId: ID!): Boolean! @auth(roles: [INVESTOR]) @audited(action: "investor.remove_seat")
}
MutationrecordComplianceActivity
recordComplianceActivity(input: ComplianceActivityInput!): ComplianceActivity! @auth(roles: [OWNER, ADMIN])
MutationvoidComplianceActivity
voidComplianceActivity(id: ID!, reason: String!): Boolean! @auth(roles: [OWNER, ADMIN])
MutationconnectTwilio
connectTwilio(input: TwilioConnectionInput!): MessagingConnectionStatus! @auth(roles: [OWNER]) @stepUp @rateLimit(bucket: "messaging.manage")
MutationconnectSendGrid
connectSendGrid(input: SendGridConnectionInput!): MessagingConnectionStatus! @auth(roles: [OWNER]) @stepUp @rateLimit(bucket: "messaging.manage")
MutationrecheckMessagingConnection
recheckMessagingConnection(provider: MessagingProvider!): MessagingConnectionStatus! @auth(roles: [OWNER]) @stepUp @rateLimit(bucket: "messaging.manage")
MutationremoveMessagingConnection
removeMessagingConnection(provider: MessagingProvider!): MessagingConnectionStatus! @auth(roles: [OWNER]) @stepUp @rateLimit(bucket: "messaging.manage")
MutationstartStripeConnect
startStripeConnect: StripeConnectLink! @auth(roles: [OWNER]) @stepUp @rateLimit(bucket: "stripe.connect")
MutationstartPasskeyRegistration
startPasskeyRegistration: WebAuthnChallenge! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
MutationfinishPasskeyRegistration
finishPasskeyRegistration(challengeId: ID!, response: WebAuthnJSON!): SessionInfo! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
MutationstartPasskeyAuthentication
startPasskeyAuthentication: WebAuthnChallenge! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
MutationfinishPasskeyAuthentication
finishPasskeyAuthentication(challengeId: ID!, response: WebAuthnJSON!): SessionInfo! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
MutationstartTotpEnrolment

Starts TOTP enrolment (fallback factor). Allowed only where passkey enrolment is (T3).

"Starts TOTP enrolment (fallback factor). Allowed only where passkey enrolment is (T3)."
startTotpEnrolment: TotpEnrolment! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
MutationverifyTotp
verifyTotp(code: String!): SessionInfo! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR]) @rateLimit(bucket: "auth.totp")
MutationresetTotpLock

Owner recovery of another account's TOTP lock; preserves daily budget and never grants MFA.

"Owner recovery of another account's TOTP lock; preserves daily budget and never grants MFA."
resetTotpLock(principalKind: PrincipalKind!, principalId: ID!): Boolean! @auth(roles: [OWNER]) @stepUp
MutationrevokeAuthenticator

Remove one of the caller's own factors; needs step-up; never the last factor of staff/investors.

"Remove one of the caller's own factors; needs step-up; never the last factor of staff/investors."
revokeAuthenticator(id: ID!): Boolean! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR]) @stepUp @audited(action: "auth.authenticator.revoke")
MutationrevokeSession

End one of the caller's own sessions.

"End one of the caller's own sessions."
revokeSession(id: ID!): Boolean! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR]) @audited(action: "auth.session.revoke")
MutationsignOut

End only this browser session, including incomplete sign-in; safe when already signed out.

"End only this browser session, including incomplete sign-in; safe when already signed out."
signOut: Boolean! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER, INVESTOR])
Mutationregister
register(input: RegisterInput!): RegistrationResult! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotent @rateLimit(bucket: "register")
MutationverifyRegistrationEmail
verifyRegistrationEmail(token: String!): MyRegistration! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN])
MutationcancelRegistration

Idempotent (REQ-REG-04). GET renders a confirm page; this POST mutates (REQ-ATT-03).

"Idempotent (REQ-REG-04). GET renders a confirm page; this POST mutates (REQ-ATT-03)."
cancelRegistration(registrationId: ID!, scope: CancelScope = THIS): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotent
MutationacceptWaitlistOffer
acceptWaitlistOffer(registrationId: ID!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotent
MutationrejoinWaitlist
rejoinWaitlist(registrationId: ID!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotent
MutationsetClassSessionOptOut
setClassSessionOptOut(registrationId: ID!, occurrenceId: ID!, optedOut: Boolean!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
MutationbookOfficeHoursSlot

≥24 h elapsed lead (REQ-ATT-10).

"≥24 h elapsed lead (REQ-ATT-10)."
bookOfficeHoursSlot(occurrenceId: ID!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN]) @idempotent
MutationcancelOfficeHoursBooking

≥12 h elapsed notice (REQ-ATT-10).

"≥12 h elapsed notice (REQ-ATT-10)."
cancelOfficeHoursBooking(registrationId: ID!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN]) @idempotent
MutationupdateReminderPreferences
updateReminderPreferences(input: ReminderPreferencesInput!): ReminderPreferences! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
MutationsetAttendeeListOptIn
setAttendeeListOptIn(registrationId: ID!, optIn: Boolean!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR])
MutationcreateCalendarFeed

Mint a revocable per-device ICS feed token (SEC-16). Not for investors (REQ-EML-09).

"Mint a revocable per-device ICS feed token (SEC-16). Not for investors (REQ-EML-09)."
createCalendarFeed(label: String!): CalendarFeedCreated! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER]) @audited(action: "calendar_feed.create") @rateLimit(bucket: "calendar_feed")
MutationrevokeCalendarFeed
revokeCalendarFeed(id: ID!): Boolean! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, HOST, VIEWER, EVENT_MANAGER, PROGRAMME_MANAGER, ADMIN, OWNER]) @audited(action: "calendar_feed.revoke")
MutationrecordConsent
recordConsent(input: ConsentInput!): ConsentRecord! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN]) @idempotent
MutationstartCheckout

Stripe Checkout on Olivier's connected account (T9, SEC-22).

"Stripe Checkout on Olivier's connected account (T9, SEC-22)."
startCheckout(registrationId: ID!, ticketTierId: ID, promoCode: String, useAccountCredit: Boolean = false): CheckoutSession! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotent @rateLimit(bucket: "checkout")
MutationupdateCompanyProfile
updateCompanyProfile(companyId: ID!, input: CompanyProfileInput!): Company! @auth(roles: [FOUNDER, COMPANY_ADMIN])
MutationupsertTeamEntry
upsertTeamEntry(companyId: ID!, input: TeamEntryInput!): CompanyTeamEntry! @auth(roles: [FOUNDER, COMPANY_ADMIN])
MutationremoveTeamEntry
removeTeamEntry(id: ID!): Boolean! @auth(roles: [FOUNDER, COMPANY_ADMIN])
MutationsetTeamEntryVisibility

Per-founder opt-in for their own entry (REQ-CNS-01).

"Per-founder opt-in for their own entry (REQ-CNS-01)."
setTeamEntryVisibility(id: ID!, visibility: VisibilityClass!): CompanyTeamEntry! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp
MutationsaveProgressDraft

Autosave ≤ 5 s (REQ-FND-02).

"Autosave ≤ 5 s (REQ-FND-02)."
saveProgressDraft(companyId: ID!, input: ProgressDraftInput!): ProgressUpdateDraft! @auth(roles: [FOUNDER, COMPANY_ADMIN])
MutationdiscardProgressDraft
discardProgressDraft(draftId: ID!): Boolean! @auth(roles: [FOUNDER, COMPANY_ADMIN])
MutationpublishProgressUpdate
publishProgressUpdate(draftId: ID!): ProgressUpdate! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp @idempotent @audited(action: "progress.publish")
MutationamendProgressUpdate

New immutable version; investors see "Edited <date>" (REQ-FND-03).

"New immutable version; investors see \"Edited <date>\" (REQ-FND-03)."
amendProgressUpdate(updateId: ID!, input: ProgressDraftInput!): ProgressUpdate! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp @idempotent @audited(action: "progress.amend")
MutationdefineKpi
defineKpi(companyId: ID!, input: KpiDefinitionInput!): KpiDefinition! @auth(roles: [FOUNDER, COMPANY_ADMIN])
MutationarchiveKpi
archiveKpi(kpiDefinitionId: ID!): KpiDefinition! @auth(roles: [FOUNDER, COMPANY_ADMIN])
MutationsetKpiVisibility
setKpiVisibility(kpiDefinitionId: ID!, visibility: VisibilityClass!): KpiDefinition! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp
MutationupsertMilestone
upsertMilestone(companyId: ID!, input: MilestoneInput!): Milestone! @auth(roles: [FOUNDER, COMPANY_ADMIN])
MutationsetMilestoneVisibility
setMilestoneVisibility(milestoneId: ID!, visibility: VisibilityClass!): Milestone! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp
MutationsetFundraisingStatus
setFundraisingStatus(companyId: ID!, status: FundraisingStatus): Company! @auth(roles: [COMPANY_ADMIN]) @stepUp
MutationsetCompanyFieldVisibility
setCompanyFieldVisibility(companyId: ID!, field: CompanyVisibilityField!, visibility: VisibilityClass!): Company! @auth(roles: [COMPANY_ADMIN]) @stepUp
MutationsetCompanyInvestorVisibility

Widening to investors needs step-up; turning off never does (REQ-FND-10).

"Widening to investors needs step-up; turning off never does (REQ-FND-10)."
setCompanyInvestorVisibility(companyId: ID!, visible: Boolean!): Company! @auth(roles: [COMPANY_ADMIN])
MutationrequestDeckUpload
requestDeckUpload(companyId: ID!, input: DeckUploadInput!): DeckUploadTicket! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUp @rateLimit(bucket: "deck.upload")
MutationconfirmDeckUpload
confirmDeckUpload(pitchMaterialId: ID!): PitchMaterial! @auth(roles: [FOUNDER, COMPANY_ADMIN])
MutationsetDeckOptions
setDeckOptions(pitchMaterialId: ID!, downloadAllowed: Boolean, visibility: VisibilityClass): PitchMaterial! @auth(roles: [COMPANY_ADMIN]) @stepUp
MutationgrantDisclosure

Append-only grant row (REQ-CNS-01, SEC-36).

"Append-only grant row (REQ-CNS-01, SEC-36)."
grantDisclosure(input: DisclosureGrantInput!): DisclosureGrant! @auth(roles: [COMPANY_ADMIN]) @stepUp @idempotent @audited(action: "grant.grant")
MutationwithdrawDisclosure

Effective on the next request; no step-up needed to narrow (REQ-FND-10, SEC-52).

"Effective on the next request; no step-up needed to narrow (REQ-FND-10, SEC-52)."
withdrawDisclosure(companyId: ID!, scope: GrantScope!): DisclosureGrant! @auth(roles: [COMPANY_ADMIN]) @idempotent @audited(action: "grant.withdraw")
MutationpauseInvestorVisibility
pauseInvestorVisibility(companyId: ID!): [DisclosureGrant!]! @auth(roles: [COMPANY_ADMIN]) @audited(action: "grant.pause")
MutationresumeInvestorVisibility
resumeInvestorVisibility(companyId: ID!): [DisclosureGrant!]! @auth(roles: [COMPANY_ADMIN]) @stepUp @audited(action: "grant.resume")
MutationblockInvestorFirm
blockInvestorFirm(companyId: ID!, investorOrgId: ID!): FounderBlock! @auth(roles: [COMPANY_ADMIN]) @audited(action: "founder_block.add")
MutationunblockInvestorFirm
unblockInvestorFirm(founderBlockId: ID!): Boolean! @auth(roles: [COMPANY_ADMIN]) @stepUp @audited(action: "founder_block.remove")
MutationunpublishOwnUpdate

A founder takes their own update off investor view (REQ-FND-03/10). Narrowing: no step-up. Amend republishes.

"A founder takes their own update off investor view (REQ-FND-03/10). Narrowing: no step-up. Amend republishes."
unpublishOwnUpdate(updateId: ID!): ProgressUpdate! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @audited(action: "progress.founder_unpublish")
MutationsetUpdateCadence

Lengthen (or restore) the reminder cadence; never below Olivier's minimum (REQ-PRG-05).

"Lengthen (or restore) the reminder cadence; never below Olivier's minimum (REQ-PRG-05)."
setUpdateCadence(companyId: ID!, months: Int!): UpdateCadence! @auth(roles: [COMPANY_ADMIN]) @audited(action: "company.cadence")
MutationsnoozeCadenceReminder

Snooze this cycle's reminder, one tap (REQ-FND-07). Idempotent.

"Snooze this cycle's reminder, one tap (REQ-FND-07). Idempotent."
snoozeCadenceReminder(companyId: ID!): UpdateCadence! @auth(roles: [FOUNDER, COMPANY_ADMIN])
MutationrespondToIntroRequest

Accept / decline / ask Olivier to hold. Contact details are never revealed (REQ-FND-11).

"Accept / decline / ask Olivier to hold. Contact details are never revealed (REQ-FND-11)."
respondToIntroRequest(introRequestId: ID!, decision: FounderIntroDecision!, declineReason: String): IntroRequest! @auth(roles: [COMPANY_ADMIN]) @audited(action: "intro.founder_decision")
MutationapplyToPitch

Apply to pitch with the existing profile and deck (REQ-FND-14, REQ-ADM-07).

"Apply to pitch with the existing profile and deck (REQ-FND-14, REQ-ADM-07)."
applyToPitch(input: ApplicationInput!): Application! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN]) @idempotent
MutationexportCompanyData

DSAR-style JSON for the company's own data (SEC-24).

"DSAR-style JSON for the company's own data (SEC-24)."
exportCompanyData(companyId: ID!): RosterExport! @auth(roles: [COMPANY_ADMIN]) @stepUp @audited(action: "company.export") @rateLimit(bucket: "export")
MutationcreateEventSeries
createEventSeries(input: EventSeriesInput!): EventSeries! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER]) @idempotent
MutationupdateEventSeries

THIS / THIS_AND_FOLLOWING / ALL; 409 STALE_VERSION on a stale expectedVersion (REQ-ADM-10).

"THIS / THIS_AND_FOLLOWING / ALL; 409 STALE_VERSION on a stale expectedVersion (REQ-ADM-10)."
updateEventSeries(
  seriesId: ID!
  expectedVersion: Int!
  input: EventSeriesPatch!
  scope: EditScope = THIS
  fromOccurrenceId: ID
  notifyAttendees: Boolean = true
  "ALL temporal edits: also retime occurrences that have their own time (QA-EDT-06)."
  resetExceptions: Boolean = false
  "Plan only: return the impact without writing (shown before the admin confirms)."
  dryRun: Boolean = false
): EditResult! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationupdateOccurrence
updateOccurrence(occurrenceId: ID!, expectedVersion: Int!, input: OccurrencePatch!, notifyAttendees: Boolean = true): EditResult! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationupdateOccurrenceDescription

Hosts edit description/agenda only (REQ-HST-03).

"Hosts edit description/agenda only (REQ-HST-03)."
updateOccurrenceDescription(occurrenceId: ID!, expectedVersion: Int!, description: String!): Occurrence! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
MutationundoPendingNotification

Cancel the 10 s pending update notification (REQ-ADM-09).

"Cancel the 10 s pending update notification (REQ-ADM-09)."
undoPendingNotification(pendingNotificationId: ID!): Boolean! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationpublishSeries
publishSeries(seriesId: ID!, expectedVersion: Int!): EventSeries! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationcloneSeries
cloneSeries(seriesId: ID!): EventSeries! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER]) @idempotent
MutationcancelOccurrence

Cancel ≠ delete; past occurrences with attendance are never deleted (REQ-ADM-10).

"Cancel ≠ delete; past occurrences with attendance are never deleted (REQ-ADM-10)."
cancelOccurrence(occurrenceId: ID!, reason: String, notifyAttendees: Boolean = true, settlements: [CancellationSettlementInput!]): Occurrence! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationcancelSeries
cancelSeries(seriesId: ID!, scope: EditScope = ALL, fromOccurrenceId: ID, reason: String, settlements: [CancellationSettlementInput!]): EditResult! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationsetCapacity

Choose-who flow when decreasing below confirmed (REQ-CAP-04).

"Choose-who flow when decreasing below confirmed (REQ-CAP-04)."
setCapacity(seriesId: ID, occurrenceId: ID, capacity: Int, releaseRegistrationIds: [ID!]): Occurrence @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationupdateEventBrief
updateEventBrief(seriesId: ID!, input: EventBriefInput!): EventSeries! @auth(roles: [OWNER, ADMIN])
MutationupsertVenue
upsertVenue(id: ID, input: VenueInput!): Venue! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationarchiveVenue
archiveVenue(id: ID!): Boolean! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationassignHost
assignHost(seriesId: ID!, occurrenceId: ID, staffUserId: ID, guestDisplayName: String): Host! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationremoveHost
removeHost(hostId: ID!): Boolean! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationupsertRegistrationQuestion
upsertRegistrationQuestion(seriesId: ID!, id: ID, label: String!, kind: String!, options: [String!], required: Boolean!): RegistrationQuestion! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationupsertTicketTier
upsertTicketTier(id: ID, input: TicketTierInput!): TicketTier! @auth(roles: [OWNER, ADMIN])
MutationupsertPromoCode
upsertPromoCode(id: ID, input: PromoCodeInput!): PromoCode! @auth(roles: [OWNER, ADMIN])
MutationsetRefundPolicy

Attendee-cancellation refund policy shown on the event page (T9, REQ-STM-03).

"Attendee-cancellation refund policy shown on the event page (T9, REQ-STM-03)."
setRefundPolicy(seriesId: ID!, input: RefundPolicyInput!): RefundPolicy! @auth(roles: [OWNER, ADMIN]) @audited(action: "payment.refund_policy")
MutationrequestMaterialUpload
requestMaterialUpload(input: MaterialUploadInput!): UploadTicket! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
MutationconfirmMaterialUpload
confirmMaterialUpload(objectId: ID!): Material! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
MutationsetRecording
setRecording(occurrenceId: ID!, url: URL, access: MaterialAccessLevel): Occurrence! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
MutationaddRegistrant

Obeys the capacity invariant (REQ-CAP-07).

"Obeys the capacity invariant (REQ-CAP-07)."
addRegistrant(input: ManualRegistrationInput!): RosterEntry! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER]) @idempotent
MutationapproveRegistration
approveRegistration(registrationId: ID!): RosterEntry! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationdeclineRegistration
declineRegistration(registrationId: ID!, message: String): RosterEntry! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationpromoteFromWaitlist
promoteFromWaitlist(registrationId: ID!): RosterEntry! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationcancelRegistrationAsOrganizer
cancelRegistrationAsOrganizer(registrationId: ID!, reason: String): RosterEntry! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
MutationmarkAttendance

Bulk attendance; flips allowed within 7 days (REQ-ADM-12, REQ-STM-02).

"Bulk attendance; flips allowed within 7 days (REQ-ADM-12, REQ-STM-02)."
markAttendance(occurrenceId: ID!, marks: [AttendanceInput!]!): [RosterEntry!]! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
MutationcloseAttendance
closeAttendance(occurrenceId: ID!): Occurrence! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST])
MutationsyncCheckIns

Offline check-in queue, idempotent per clientId (REQ-ADM-20).

"Offline check-in queue, idempotent per clientId (REQ-ADM-20)."
syncCheckIns(items: [CheckInSyncItem!]!): CheckInSyncResult! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, HOST]) @idempotent
MutationrevealRegistrantEmail

Reveal one registrant's email (masked by default); audited (REQ-ADM-11).

"Reveal one registrant's email (masked by default); audited (REQ-ADM-11)."
revealRegistrantEmail(registrationId: ID!): String! @pii @auth(roles: [OWNER, ADMIN, EVENT_MANAGER]) @audited(action: "roster.reveal_email") @rateLimit(bucket: "roster.reveal")
MutationexportRoster

THE roster export: Owner only, step-up, rate-limited, audited, other Owners notified ≤ 5 min (REQ-ADM-13).

"THE roster export: Owner only, step-up, rate-limited, audited, other Owners notified ≤ 5 min (REQ-ADM-13)."
exportRoster(occurrenceId: ID!): RosterExport! @auth(roles: [OWNER]) @stepUp @audited(action: "roster.export") @rateLimit(bucket: "export")
MutationsendEventMessage

Per-event transactional message, ≤ 1/event/hour, Owner-notified. No newsletters (REQ-ADM-19).

"Per-event transactional message, ≤ 1/event/hour, Owner-notified. No newsletters (REQ-ADM-19)."
sendEventMessage(occurrenceId: ID!, subject: String!, body: String!): Boolean! @auth(roles: [OWNER, ADMIN]) @idempotent @audited(action: "event.message") @rateLimit(bucket: "event.message")
MutationdecideApplication
decideApplication(applicationId: ID!, outcome: ApplicationOutcome!): Application! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER, PROGRAMME_MANAGER])
MutationrefundRegistration

Organizer cancellation → full refund regardless of window (REQ-STM-05).

"Organizer cancellation → full refund regardless of window (REQ-STM-05)."
refundRegistration(registrationId: ID!, reason: String!): PaymentSummary! @auth(roles: [OWNER, ADMIN]) @stepUp @idempotent @audited(action: "payment.refund")
MutationrequestImportUpload
requestImportUpload(source: ImportSource!): UploadTicket! @auth(roles: [OWNER, ADMIN])
MutationstartImport

Dry run first; sends zero emails (REQ-MIG-02/07).

"Dry run first; sends zero emails (REQ-MIG-02/07)."
startImport(input: ImportInput!): ImportJob! @auth(roles: [OWNER, ADMIN]) @idempotent
MutationcommitImport
commitImport(importJobId: ID!): ImportJob! @auth(roles: [OWNER, ADMIN]) @idempotent @audited(action: "import.commit")
MutationsetImportedEventReminders

(T11) Reminders for an imported event's imported registrations: opt-in, default off (REQ-MIG-02, QA-MIG-12).

"(T11) Reminders for an imported event's imported registrations: opt-in, default off (REQ-MIG-02, QA-MIG-12)."
setImportedEventReminders(seriesId: ID!, enabled: Boolean!): Boolean! @auth(roles: [OWNER, ADMIN]) @audited(action: "import.reminders")
MutationupdateOrganizerSettings
updateOrganizerSettings(input: OrganizerSettingsInput!): OrganizerSettings! @auth(roles: [OWNER, ADMIN]) @audited(action: "settings.update")
MutationupdateVisibilityPolicy
updateVisibilityPolicy(input: VisibilityPolicyInput!): VisibilityPolicy! @auth(roles: [OWNER]) @stepUp @audited(action: "visibility_policy.update")
MutationinviteStaff
inviteStaff(email: EmailAddress!, displayName: String!, roles: [OrganizerRole!]!): StaffMember! @auth(roles: [OWNER]) @stepUp @audited(action: "staff.invite")
MutationgrantRole
grantRole(staffUserId: ID!, role: OrganizerRole!): StaffMember! @auth(roles: [OWNER]) @stepUp @audited(action: "role.grant")
MutationrevokeRole
revokeRole(staffUserId: ID!, role: OrganizerRole!, reason: String!): StaffMember! @auth(roles: [OWNER]) @stepUp @audited(action: "role.revoke")
MutationexportMemberData

DSAR for one person (SEC-24); AF roles have no equivalent.

"DSAR for one person (SEC-24); AF roles have no equivalent."
exportMemberData(attendeeId: ID!): RosterExport! @auth(roles: [OWNER]) @stepUp @audited(action: "member.export") @rateLimit(bucket: "export")
MutationrequestMemberErasure

Pseudonymise within 30 days; reminders stop immediately (REQ-ATT-11).

"Pseudonymise within 30 days; reminders stop immediately (REQ-ATT-11)."
requestMemberErasure(attendeeId: ID!, reason: String!): Boolean! @auth(roles: [OWNER]) @stepUp @audited(action: "member.erasure")
MutationcreateCompany
createCompany(input: CreateCompanyInput!): Company! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @idempotent
MutationupdateCompanyAdminFields
updateCompanyAdminFields(companyId: ID!, input: CompanyAdminPatch!): Company! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "company.admin_update")
MutationpromoteProspect
promoteProspect(companyId: ID!): Company! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
MutationinviteFounder
inviteFounder(companyId: ID!, email: EmailAddress!, displayName: String!, role: CompanyMemberRole!): CompanyMember! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "company.invite_founder")
MutationremoveCompanyMember
removeCompanyMember(companyMemberId: ID!, reason: String!): CompanyMember! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "company.remove_member")
MutationreassignCompanyAdmin

With a reason; all company members notified (REQ-CO-02, SEC-53).

"With a reason; all company members notified (REQ-CO-02, SEC-53)."
reassignCompanyAdmin(companyId: ID!, companyMemberId: ID!, reason: String!): CompanyMember! @auth(roles: [OWNER]) @stepUp @audited(action: "company.reassign_admin")
MutationmoveCompanyIntake

Requires the IntakeMoveImpact preview to have been shown (REQ-CO-03).

"Requires the IntakeMoveImpact preview to have been shown (REQ-CO-03)."
moveCompanyIntake(companyId: ID!, toIntakeId: ID!, confirmedImpact: Boolean!): Company! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "company.move_intake")
MutationarchiveCompany
archiveCompany(companyId: ID!, reason: String!): Company! @auth(roles: [OWNER, ADMIN]) @audited(action: "company.archive")
MutationapproveCompanyDeletion

Deletion needs founder + Owner dual approval (REQ-CO-06, SEC-53).

"Deletion needs founder + Owner dual approval (REQ-CO-06, SEC-53)."
approveCompanyDeletion(companyId: ID!): Company! @auth(roles: [OWNER, COMPANY_ADMIN]) @stepUp @audited(action: "company.delete_approval")
MutationcreateIntake
createIntake(name: String!, startsOn: LocalDate!, endsOn: LocalDate, accessGroups: [String!]): CohortIntake! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
MutationupsertMilestoneTemplate
upsertMilestoneTemplate(intakeId: ID!, id: ID, title: String!, position: Int!): Boolean! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
MutationaddAdminNote
addAdminNote(companyId: ID!, body: String!): AdminNote! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
MutationsetAdminFlag
setAdminFlag(companyId: ID!, kind: AdminFlagKind!, reason: String): AdminFlag! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
MutationclearAdminFlag
clearAdminFlag(adminFlagId: ID!): AdminFlag! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
MutationunpublishProgressUpdate

Reason shown to the founder (REQ-CO-06).

"Reason shown to the founder (REQ-CO-06)."
unpublishProgressUpdate(updateId: ID!, reason: String!): ProgressUpdate! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "progress.unpublish")
MutationrequestUpdateClarification

Private note or structured clarification; ADMIN_ONLY (REQ-PRG-06).

"Private note or structured clarification; ADMIN_ONLY (REQ-PRG-06)."
requestUpdateClarification(updateId: ID!, note: String!): Boolean! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
MutationactOnIntroRequest

Pass / hold / decline. No bulk actions (REQ-CNS-04).

"Pass / hold / decline. No bulk actions (REQ-CNS-04)."
actOnIntroRequest(introRequestId: ID!, action: OlivierIntroAction!, note: String): IntroRequest! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "intro.olivier_action")
MutationmarkIntroduced

Requires confirming the introduction email was sent off-platform (REQ-CNS-04, REQ-VC-12).

"Requires confirming the introduction email was sent off-platform (REQ-CNS-04, REQ-VC-12)."
markIntroduced(introRequestId: ID!, introductionEmailSent: Boolean!): IntroRequest! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER]) @audited(action: "intro.introduced")
MutationadmitInvestorOrg
admitInvestorOrg(input: AdmitInvestorOrgInput!): InvestorOrg! @auth(roles: [OWNER]) @stepUp @audited(action: "investor_org.admit")
MutationinviteInvestor

Firm work-email domain; 7-day invitation (REQ-VC-01).

"Firm work-email domain; 7-day invitation (REQ-VC-01)."
inviteInvestor(input: InviteInvestorInput!): InvestorMemberAdmin! @auth(roles: [OWNER]) @stepUp @audited(action: "investor.invite")
MutationsuspendInvestor

Kills sessions within one request (REQ-VC-04).

"Kills sessions within one request (REQ-VC-04)."
suspendInvestor(memberId: ID!, reason: String!): InvestorMemberAdmin! @auth(roles: [OWNER]) @audited(action: "investor.suspend")
MutationrevokeInvestor
revokeInvestor(memberId: ID!, reason: String!): InvestorMemberAdmin! @auth(roles: [OWNER]) @audited(action: "investor.revoke")
MutationunlockInvestor

Owner unlock after an anomaly soft-lock, with reason (REQ-VC-18).

"Owner unlock after an anomaly soft-lock, with reason (REQ-VC-18)."
unlockInvestor(memberId: ID!, reason: String!): InvestorMemberAdmin! @auth(roles: [OWNER]) @stepUp @audited(action: "investor.unlock")
MutationrecertifyInvestors
recertifyInvestors(memberIds: [ID!]!): [InvestorMemberAdmin!]! @auth(roles: [OWNER]) @audited(action: "investor.recertify")
MutationpublishTermsOfAccess

New version blocks investor access until re-accepted (SEC-41).

"New version blocks investor access until re-accepted (SEC-41)."
publishTermsOfAccess(input: TermsOfAccessInput!): TermsOfAccess! @auth(roles: [OWNER]) @stepUp @audited(action: "terms.publish")
MutationacceptTermsOfAccess
acceptTermsOfAccess(versionHash: String!): InvestorMe! @auth(roles: [INVESTOR]) @audited(action: "terms.accept")
MutationmarkInvestorHomeVisited
markInvestorHomeVisited: InvestorMe! @auth(roles: [INVESTOR])
MutationsetDigestOptIn
setDigestOptIn(optIn: Boolean!): InvestorMe! @auth(roles: [INVESTOR])
MutationfollowCompany
followCompany(companyId: ID!): InvestorCompanyCard! @auth(roles: [INVESTOR])
MutationunfollowCompany
unfollowCompany(companyId: ID!): InvestorCompanyCard! @auth(roles: [INVESTOR])
MutationrequestIntro

One open request per (investor, company); monthly cap (REQ-VC-11).

"One open request per (investor, company); monthly cap (REQ-VC-11)."
requestIntro(input: IntroRequestInput!): InvestorIntroState! @auth(roles: [INVESTOR]) @idempotent @rateLimit(bucket: "investor.intro") @audited(action: "intro.request")
MutationwithdrawIntroRequest
withdrawIntroRequest(companyId: ID!): InvestorIntroState! @auth(roles: [INVESTOR])
MutationrequestDeckDownload

Only if the founder enabled download; per-viewer watermarked PDF (REQ-VC-09).

"Only if the founder enabled download; per-viewer watermarked PDF (REQ-VC-09)."
requestDeckDownload(deckId: ID!): DeckDownload! @auth(roles: [INVESTOR]) @stepUp @audited(action: "deck.download") @rateLimit(bucket: "investor.deck_download")
MutationremoveInvestorSeat

Firm org admin manages seats within the cap; Olivier can override (REQ-VC-16).

"Firm org admin manages seats within the cap; Olivier can override (REQ-VC-16)."
removeInvestorSeat(memberId: ID!): Boolean! @auth(roles: [INVESTOR]) @audited(action: "investor.remove_seat")
EnumAttendeeIntroPreference
enum AttendeeIntroPreference {
  OPEN
  LIMITED
  NONE
}
EnumEventIntroductionState
enum EventIntroductionState {
  PENDING
  ACCEPTED
  DECLINED
  INTRODUCED
}
TypeAttendeeProfile
type AttendeeProfile {
  id: ID!
  displayName: String! @personal @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, OWNER, ADMIN, PROGRAMME_MANAGER])
  bio: String @personal @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, OWNER, ADMIN, PROGRAMME_MANAGER])
  introPreference: AttendeeIntroPreference!
  introConditions: String @personal @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, OWNER, ADMIN, PROGRAMME_MANAGER])
}
InputAttendeeProfileInput
input AttendeeProfileInput {
  bio: String
  introPreference: AttendeeIntroPreference!
  introConditions: String
}
TypeEventAttendeePage
type EventAttendeePage {
  nodes: [AttendeeProfile!]!
  nextCursor: ID
}
TypeEventIntroduction
type EventIntroduction {
  id: ID!
  occurrenceId: ID!
  requester: AttendeeProfile!
  recipient: AttendeeProfile!
  reason: String! @personal @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, OWNER, ADMIN, PROGRAMME_MANAGER])
  state: EventIntroductionState!
  createdAt: DateTime!
}
TypeQuery
extend type Query {
  myAttendeeProfile: AttendeeProfile! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  eventAttendees(occurrenceId: ID!, first: Int = 50, after: ID): EventAttendeePage! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  eventAttendeeProfile(occurrenceId: ID!, attendeeId: ID!): AttendeeProfile! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  myEventIntroductions: [EventIntroduction!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  eventIntroductionQueue(first: Int = 100): [EventIntroduction!]! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
}
QuerymyAttendeeProfile
myAttendeeProfile: AttendeeProfile! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
QueryeventAttendees
eventAttendees(occurrenceId: ID!, first: Int = 50, after: ID): EventAttendeePage! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
QueryeventAttendeeProfile
eventAttendeeProfile(occurrenceId: ID!, attendeeId: ID!): AttendeeProfile! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
QuerymyEventIntroductions
myEventIntroductions: [EventIntroduction!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
QueryeventIntroductionQueue
eventIntroductionQueue(first: Int = 100): [EventIntroduction!]! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
TypeMutation
extend type Mutation {
  updateMyAttendeeProfile(input: AttendeeProfileInput!): AttendeeProfile! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  requestEventIntroduction(occurrenceId: ID!, attendeeId: ID!, reason: String!): EventIntroduction! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  respondToEventIntroduction(id: ID!, accept: Boolean!): EventIntroduction! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
  markEventIntroductionIntroduced(id: ID!): EventIntroduction! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
}
MutationupdateMyAttendeeProfile
updateMyAttendeeProfile(input: AttendeeProfileInput!): AttendeeProfile! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
MutationrequestEventIntroduction
requestEventIntroduction(occurrenceId: ID!, attendeeId: ID!, reason: String!): EventIntroduction! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
MutationrespondToEventIntroduction
respondToEventIntroduction(id: ID!, accept: Boolean!): EventIntroduction! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
MutationmarkEventIntroductionIntroduced
markEventIntroductionIntroduced(id: ID!): EventIntroduction! @auth(roles: [OWNER, ADMIN, PROGRAMME_MANAGER])
EnumCancellationSettlementMethod
enum CancellationSettlementMethod {
  REFUND
  CREDIT
}
InputCancellationSettlementInput
input CancellationSettlementInput {
  registrationId: ID!
  method: CancellationSettlementMethod!
  amountCents: Int!
}
TypeCancellationPayment
type CancellationPayment {
  registrationId: ID!
  displayName: String! @personal @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  currency: String!
  availableCents: Int!
  suggestedAmountCents: Int
  classBooking: Boolean!
}
TypeAccountCreditBalance
type AccountCreditBalance {
  currency: String!
  balanceCents: Int!
  frozen: Boolean!
}
TypeQuery
extend type Query {
  cancellationPreview(occurrenceId: ID!): [CancellationPayment!]! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
  myAccountCredits: [AccountCreditBalance!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])
}
QuerycancellationPreview
cancellationPreview(occurrenceId: ID!): [CancellationPayment!]! @auth(roles: [OWNER, ADMIN, EVENT_MANAGER])
QuerymyAccountCredits
myAccountCredits: [AccountCreditBalance!]! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN])

Schema SHA-256: a9ced2d099d0c4482a6af5f83297b5e0ccf5cf746aa61b3c068defe8d2f58469