Enrolment gating and staff overrides
Active enrolment (and waitlist to active conversion) runs a single evaluation that gathers all blocking issues at once rather than failing on the first check. The API exposes both a preview (dry run, HTTP 200) and enforce paths that return a problem details document when rules still block the action.
Gates evaluated (active enrolment)
Section titled “Gates evaluated (active enrolment)”The following checks run for create active and waitlist to active:
- Age - Activity minimum/maximum age from effective fields; members without a date of birth may fail with
enrollments.date_of_birth_requiredwhen any bound applies. - Prerequisites - Level prerequisites via completed levels, required skills, or historical enrolments;
MemberLevelOverridebypasses prerequisite checks for a level. - Enrolment policies - Required policies for the target activity, scoped via family billing context; if the member is in more than one family, policy evaluation may require an explicit
family_id(enrollments.family_context_required). - Capacity - Peak concurrent spot-consuming enrolments over the proposed
datetime_rangemust not exceed effective activity capacity. If any instant in that window would be over capacity (including future-dated enrolments already on the activity),enrollments.capacity_fullblocks placement (unless overridden by staff). Blocking issues may includemeta.peak_count,meta.capacity,meta.peak_at, andmeta.proposed_upper_source.
Each issue is returned with severity (for example blocking), human-readable title / detail, optional pointer and meta, plus can_override and required_permission after annotation.
Staff overrides
Section titled “Staff overrides”Staff with the correct Django permission can attest that a specific gate may be bypassed for a given request by sending staff_overrides: a map from issue code to a truthy value (for example { "enrollments.capacity_full": true }).
Rules:
- Overrides apply per issue code. If
staff_overrides[code]is not set, the issue remains. - The server checks
required_permissionfor that code. If the user lacks it, the issue is replaced withenrollments.insufficient_permission_to_override(meta.original_coderecords the gate). - Warnings are not overridable via this map (
can_overridestays false).
Permissions used for enrolment gates (codenames on ActivityEnrollment):
| Gate area | Permission codename |
|---|---|
| Age / DOB | activities.override_enrollment_age |
| Prerequisites | activities.override_enrollment_prerequisites |
| Policies | activities.override_enrollment_policies |
| Capacity | activities.override_enrollment_capacity |
Preview vs enforce
Section titled “Preview vs enforce”Capacity look-ahead
Section titled “Capacity look-ahead”Capacity uses a Postgres tstzrange peak query over the proposed enrolment window (datetime_range on the preview or create payload). Which enrolment types count (active, casual, trial, makeup) is configurable per tenant, location, and category.
When the proposed upper bound is omitted (ongoing enrolment), the server normalises the window using sibling enrolments on the activity (latest finite end or latest start), or an open-ended range when none exist.
Waitlist enrolments do not consume capacity today. Committed destination enrolments from transfers are included via their stored datetime_range rows.
Eligibility preview (HTTP 200)
Section titled “Eligibility preview (HTTP 200)”POST .../eligibility-preview evaluates gates and returns:
issues: the same annotated issue objects you would see inside a problemerrorsarray (field names align for client reuse).can_proceed:truewhen there are no blocking issues.
The preview does not mutate enrolments. It does not apply staff_overrides; it only reflects whether the current actor is staff for can_override hints.
Create enrolment / convert waitlist (enforce)
Section titled “Create enrolment / convert waitlist (enforce)”Creating an active enrolment or converting waitlist to active applies staff_overrides for staff users. If blocking issues remain, the API responds with 422 Unprocessable Entity, Content-Type: application/problem+json, and problem type:
https://docs.keja.co/troubleshooting/status-codes/enrollments.gates_failed
See the dedicated page for that type and full JSON shape: Enrolment gates failed (enrollments.gates_failed).
Client integration tips
Section titled “Client integration tips”- Drive UI and retry logic from each issue’s
code,can_override, andrequired_permission, not from parsingdetailstrings. - For policy gates when a member belongs to multiple families, pass
family_idon create/preview payloads when the API expects family-scoped policy context. - Re-run preview after changes (DOB, policy acceptance, family selection) before submitting an enrolment.