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 | OBJECTDirective@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_DEFINITIONDirective@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_DEFINITIONDirective@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_DEFINITIONDirective@idempotent
Requires the Idempotency-Key header (REQ-REG-02).
"Requires the Idempotency-Key header (REQ-REG-02)."
directive @idempotent on FIELD_DEFINITIONDirective@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_DEFINITIONDirective@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_DEFINITIONDirective@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_DEFINITIONScalarDateTime
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 DateTimeScalarLocalDateTime
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 LocalDateTimeScalarLocalDate
Calendar date YYYY-MM-DD.
"Calendar date YYYY-MM-DD."
scalar LocalDateScalarTimeZone
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 TimeZoneScalarEmailAddress
Email address (input only, except @pii fields).
"Email address (input only, except @pii fields)."
scalar EmailAddressScalarWebAuthnJSON
Opaque WebAuthn JSON (PublicKeyCredential options/responses), passed through to T3.
"Opaque WebAuthn JSON (PublicKeyCredential options/responses), passed through to T3."
scalar WebAuthnJSONEnumRole
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
}EnumEventVisibility
enum EventVisibility {
PUBLIC
UNLISTED
COMMUNITY
INVITE_ONLY
INVESTOR
}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
}EnumOrganizerRole
enum OrganizerRole {
OWNER
ADMIN
EVENT_MANAGER
PROGRAMME_MANAGER
HOST
VIEWER
}EnumCompanyStatus
enum CompanyStatus {
PROSPECT
APPLICANT
ACTIVE
ALUMNI
PAUSED
WITHDRAWN
}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
}EnumMilestoneStatus
enum MilestoneStatus {
NOT_STARTED
IN_PROGRESS
DONE
BLOCKED
}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
}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
}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
}EnumApplicationOutcome
enum ApplicationOutcome {
PENDING
SELECTED
NOT_SELECTED
WAITLISTED
}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
}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
}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
}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
}EnumFounderIntroDecision
enum FounderIntroDecision {
ACCEPT
DECLINE
ASK_OLIVIER_TO_HOLD
}EnumOlivierIntroAction
enum OlivierIntroAction {
PASS_TO_FOUNDER
HOLD
DECLINE
}EnumImportJobStatus
enum ImportJobStatus {
UPLOADED
DRY_RUN_READY
COMMITTING
COMMITTED
FAILED
}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!
}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!
}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
}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!]!
}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
}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!
}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!]!
}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!]!
}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
}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])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])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")MutationrequestManageLink
requestManageLink(email: EmailAddress!): Boolean! @auth(roles: [ANON, ATTENDEE, FOUNDER, COMPANY_ADMIN]) @rateLimit(bucket: "auth.link")MutationrequestStaffSignInLink
requestStaffSignInLink(email: EmailAddress!): Boolean! @auth(roles: [ANON]) @rateLimit(bucket: "auth.link")MutationexchangeSignedLink
POST-consumes a single-use signed link; sets the session cookie (SEC-13).
"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")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]) @stepUpMutationrevokeAuthenticator
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]) @idempotentMutationacceptWaitlistOffer
acceptWaitlistOffer(registrationId: ID!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotentMutationrejoinWaitlist
rejoinWaitlist(registrationId: ID!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN, INVESTOR]) @idempotentMutationsetClassSessionOptOut
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]) @idempotentMutationcancelOfficeHoursBooking
≥12 h elapsed notice (REQ-ATT-10).
"≥12 h elapsed notice (REQ-ATT-10)."
cancelOfficeHoursBooking(registrationId: ID!): MyRegistration! @auth(roles: [ATTENDEE, FOUNDER, COMPANY_ADMIN]) @idempotentMutationupdateReminderPreferences
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]) @idempotentMutationstartCheckout
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]) @stepUpMutationsaveProgressDraft
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]) @stepUpMutationupsertMilestone
upsertMilestone(companyId: ID!, input: MilestoneInput!): Milestone! @auth(roles: [FOUNDER, COMPANY_ADMIN])MutationsetMilestoneVisibility
setMilestoneVisibility(milestoneId: ID!, visibility: VisibilityClass!): Milestone! @auth(roles: [FOUNDER, COMPANY_ADMIN]) @stepUpMutationsetFundraisingStatus
setFundraisingStatus(companyId: ID!, status: FundraisingStatus): Company! @auth(roles: [COMPANY_ADMIN]) @stepUpMutationsetCompanyFieldVisibility
setCompanyFieldVisibility(companyId: ID!, field: CompanyVisibilityField!, visibility: VisibilityClass!): Company! @auth(roles: [COMPANY_ADMIN]) @stepUpMutationsetCompanyInvestorVisibility
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]) @stepUpMutationgrantDisclosure
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]) @idempotentMutationexportCompanyData
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]) @idempotentMutationupdateEventSeries
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]) @idempotentMutationcancelOccurrence
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]) @idempotentMutationapproveRegistration
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]) @idempotentMutationrevealRegistrantEmail
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]) @idempotentMutationcommitImport
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]) @idempotentMutationupdateCompanyAdminFields
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