Public Enrollment
Public Enrollment
Abschnitt betitelt „Public Enrollment“publicEnrollToCourse is the only mutation that creates a registration without an authenticated user. It is the integration point for external course portals, your school’s public website, or landing pages that list courses and accept sign-ups.
Server-side it is annotated with @Authorized(auth('Public')), which places it on a dedicated public rate-limiter and enforces strict input validation.
Mutation
Abschnitt betitelt „Mutation“mutation PublicEnrollToCourse($input: PublicEnrollmentInput!) { publicEnrollToCourse(input: $input) { status message autoLoggedIn }}input PublicEnrollmentInput { "Course GUID (the UID of the course to enroll in)" courseGuid: String!
"Optional platform GUID. Omit to use the default platform of the course listing." platformGuid: String
"Person data. Required for new registrations." person: PublicPersonInput
"OTP sign-up token issued after email verification. Required when the caller is not authenticated." signupToken: String}
input PublicPersonInput { prename: String! lastname: String! "Birth date in YYYY-MM-DD format" birthdate: String! address: PublicAddressInput!}
input PublicAddressInput { street: String! streetNr: String! city: String! "Postal code" areaCode: String! "State / Canton" state: String! country: String! email: String! phone: String!}Every field on PublicPersonInput and PublicAddressInput is required — the public form must collect all of them before submitting.
Example: anonymous sign-up
Abschnitt betitelt „Example: anonymous sign-up“{ "input": { "courseGuid": "4a9e2c17-...-b3", "signupToken": "eyJhbGciOi...", "person": { "prename": "Anna", "lastname": "Müller", "birthdate": "1998-03-21", "address": { "street": "Marktgasse", "streetNr": "12", "areaCode": "3011", "city": "Bern", "state": "BE", "country": "CH", "email": "anna.mueller@example.ch", "phone": "+41 31 123 45 67" } } }}Response:
{ "data": { "publicEnrollToCourse": { "status": "SUCCESS", "message": "Anmeldung wurde erfasst.", "autoLoggedIn": false } }}status uses the shared OperationResultStatus enum — typically SUCCESS, WARNING or ERROR. The human-readable message is safe to show directly to the visitor.
The sign-up token (OTP flow)
Abschnitt betitelt „The sign-up token (OTP flow)“signupToken is required whenever the caller is not authenticated. It is issued by the OTP verification endpoint after the visitor confirms their email address. The token binds the enrollment request to a verified email, which prevents bots from creating spurious person records.
Typical flow from the browser:
- Visitor enters email → server sends OTP and returns a challenge.
- Visitor enters the OTP code → server returns a
signupToken(JWT, short-lived). - Visitor fills out the enrollment form → browser sends
publicEnrollToCoursewithsignupToken.
If the visitor is already authenticated (recurring customer logging in before enrolling), omit signupToken and let the authenticated variant enrollToCourse handle it.
Authenticated variant
Abschnitt betitelt „Authenticated variant“When the caller already has a JWT, use enrollToCourse with the same input shape — person then defaults to the current user and can be omitted:
mutation EnrollToCourse($input: PublicEnrollmentInput!) { enrollToCourse(input: $input) { status message }}curl example (fully anonymous)
Abschnitt betitelt „curl example (fully anonymous)“curl -X POST https://your.server/graphql \ -H "Content-Type: application/json" \ --data '{ "query": "mutation($input: PublicEnrollmentInput!) { publicEnrollToCourse(input: $input) { status message } }", "variables": { "input": { "courseGuid": "4a9e2c17-...-b3", "signupToken": "eyJhbGciOi...", "person": { "prename": "Anna", "lastname": "Müller", "birthdate": "1998-03-21", "address": { "street": "Marktgasse", "streetNr": "12", "areaCode": "3011", "city": "Bern", "state": "BE", "country": "CH", "email": "anna.mueller@example.ch", "phone": "+41 31 123 45 67" } } } } }'What happens server-side
Abschnitt betitelt „What happens server-side“- The server validates the token, input and course availability.
- If the person does not exist yet (matched on email + lastname + birthdate) a new
Personrecord is created in the default person folder configured for the platform. - A
CourseRegistrationwithregistrationType: COURSEand the single course incourseIDsis created. - No
CourseParticipantis created yet — staff still has to confirm the booking. See Course Participants for that step.
Error handling
Abschnitt betitelt „Error handling“Common failure cases:
status | Cause |
|---|---|
ERROR | Invalid signupToken, expired OTP, unknown courseGuid. |
ERROR | Course is full and the platform is configured to reject waitlist. |
WARNING | Duplicate registration detected; original registration was kept. |
The message field is localized to the course listing’s platform language and is safe to display as-is.