Requirements
Requirement kinds, subjects, gates, inheritance, and how eligibility is evaluated at checkout and check-in
A Requirement is a configured condition that must be satisfied before a named gate. Changing any one of its five coordinates makes a different condition.
| Coordinate | Values |
|---|---|
| Scope | ORGANIZATION, EVENT_GROUP_SERIES, EVENT_GROUP, EVENT_SERIES, EVENT, PASS |
| Kind | IDENTITY_COMPLETE, EMAIL_VERIFIED, PHONE_VERIFIED, AFFILIATION, FORM, CONSENT |
| Subject | BUYER, PARTICIPANT, EVERY_PARTICIPANT, REGISTRATION |
| Gate | AT_SELECTION, BEFORE_PAYMENT, BEFORE_ACTIVATION, BEFORE_CHECKIN |
| Enforcement | SOFT or HARD |
Definitions are versioned: editing a Requirement archives the prior definition and writes a new one, so an evaluation can name the definition it was decided under.
Kinds
Four kinds evaluate. Two exist in the model but are not implemented.
| Kind | What the evaluator checks |
|---|---|
IDENTITY_COMPLETE | The subject Account has at least one email identity and one phone identity |
EMAIL_VERIFIED | The subject Account has an email identity, optionally restricted to the domains in kindConfig.domains |
PHONE_VERIFIED | The subject Account has a phone identity |
AFFILIATION | A matching, unexpired eligibility with the configured provider id, email domain, or claim |
FORM | Nothing. The evaluator returns UNKNOWN |
CONSENT | Nothing. The evaluator returns UNKNOWN |
The identity adapter counts every email and phone identity on the Account, including a provisional identity created by admin issuance. It does not filter on verifiedAt.
AFFILIATION is the general audience gate: a provider assertion is the highest assurance, a verified email domain is the fallback, and a claim is the loosest. The absence of a provider is what makes domain gating a fallback rather than a separate concept.
Subjects
| Subject | Resolved to |
|---|---|
BUYER | The purchasing Account |
PARTICIPANT | The Account ids supplied for the participant set |
EVERY_PARTICIPANT | The same set, required to satisfy the condition in full |
REGISTRATION | No Account. The evaluator returns UNKNOWN |
At checkout, the participant set is the buyer's Account. PARTICIPANT and EVERY_PARTICIPANT are therefore evaluated against the buyer, not against each Participant's own Account. At check-in, the scanned Participant's Account is supplied instead.
Because the buyer is often not the attendee, an audience gate is authored as EVERY_PARTICIPANT by default, but the checkout evaluation does not yet distinguish the buyer from the roster.
Gates
| Gate | Where it is evaluated | Effect |
|---|---|---|
AT_SELECTION | The anonymous Pass-lock read only | Renders a Pass locked; checkout does not check it |
BEFORE_PAYMENT | Checkout, before the Order or payment intent exists | Blocks the Order |
BEFORE_ACTIVATION | The same checkout call, alongside BEFORE_PAYMENT | Blocks the Order before payment, not after it |
BEFORE_CHECKIN | Each scan, against the scanned Participant's current Account | Blocks admission; the Pass stays issued |
AT_SELECTION is advisory. A client that calls checkout directly does not pass through it, and the server does not re-check it there.
Enforcement
SOFT
- Renders a nudge
- Records the unmet state
- Never blocks
HARD
- Blocks the command at a gate the server actually evaluates
- At checkout, returns a
409problem with the generic message "A required condition is not met for this Pass" - At check-in, records
requirements_incomplete
The 409 does not carry the Requirement's id, kind, or guidance. The evaluator collects that list internally, but the registration API replaces it with the generic problem.
Ulsav's completeness prompt is a platform-owned IDENTITY_COMPLETE Requirement with SOFT enforcement, appended to every evaluation chain. An Organization may not make it HARD; it adds its own Requirement instead.
Inheritance
Requirements apply additively down the scope hierarchy, mirroring the stored RoleAssignment scopes:
Diagram source
flowchart TD
O[Organization] --> GS[EventGroupSeries]
GS --> G[EventGroup]
G --> E[Event]
O --> RS[EventSeries]
RS --> E
O --> E
E --> P[Pass]A Pass is evaluated against the union of the Organization, ancestor group or series, Event, and Pass Requirements. There is no negation: access is strengthened by adding a Requirement, never by subtracting one.
Moving an Event changes its inherited set immediately. The move is an ordinary Event update with no confirmation step and no audit record.
Evaluation
One server-authoritative evaluator answers three questions for a subject at a gate:
- whether each applicable Requirement is
SATISFIED,UNSATISFIED, orUNKNOWN, - which artifact satisfied it,
- the aggregated hard blockers.
The same evaluator backs the anonymous Pass-lock read, checkout, and check-in. Registration creation, payment confirmation, and activation do not run it separately. Results are recomputed on every call; there is no evaluation cache.
Snapshots and freshness
Checkout writes each Registration's Requirement evaluation snapshot, including the definitions evaluated and their states. Later edits do not retroactively invalidate a confirmed Registration.
BEFORE_CHECKIN is the exception by design: it is re-evaluated at scan time against current eligibility. An affiliation that expires between purchase and the door blocks admission, and the Pass remains issued.
Interfaces
A Requirement can be created, read, edited, and archived at any of the six scope types. The create path does not verify that the scope id exists in the Organization, so a wrong or foreign id produces a definition that never matches an evaluation chain.
The API checks requirements.read and requirements.manage at Organization level, not at the Requirement's scope, and RoleAssignments have no Pass scope.
The effective lock set for a Pass is anonymously readable through GET /api/v1/events/{event_id}/pass-requirement-locks, so the register surface locks before sign-in. Checkout returns the generic 409 described above; the unmet list is not included.
Invariants
- A
HARDRequirement blocks the gates the server evaluates. It does not blockAT_SELECTIONat checkout. - A Pass is not payable while a
BEFORE_PAYMENTorBEFORE_ACTIVATIONRequirement is unmet, whatever the client sends. - An unnamed Participant cannot satisfy
BEFORE_CHECKIN, because the scan has no Account to evaluate. - Requirement versions are immutable; edits archive and replace.
- Evaluation is tenant-scoped, and a Requirement from another Organization never contributes.
Related
- Registrations — how entries and people carry Requirements
- Capabilities — the other additive hierarchy
- Check-In — admission-time gating