Bulk Enroll Attendees to Event
The bulkEnrollAttendeesToEvent mutation enrolls multiple users into a single event in one request. Use it when importing an external list of users (e.g., a course’s student roster) into an event.
Each row creates a Payment with amount: 0 (comp seat semantics). See the Event Mutations overview.
Limits
Section titled “Limits”- Maximum batch size: 200 rows per call. Larger jobs should be split into multiple calls.
- Duplicate
userIdoremailvalues within the same batch are rejected (EVENT-008).
Input Parameters
Section titled “Input Parameters”| Field | Type | Required | Description |
|---|---|---|---|
eventId | String! | Yes | ID of the event |
inputs | [AdminEventAttendeeBulkInput!]! | Yes | Per-attendee rows (max 200) |
ticketId | String | No | Ticket applied to all rows. Defaults to event.tickets.first. |
atomic | Boolean | No | When true, any row failure rolls back the entire batch. Default: false (independent rows). |
AdminEventAttendeeBulkInput
Section titled “AdminEventAttendeeBulkInput”| Field | Type | Required | Description |
|---|---|---|---|
userId | String | No* | Existing student ID |
email | String | No* | Student email; creates a placeholder user if no match |
name | String | No | Required when creating a user via email. Ignored when email matches an existing student; the stored name is preserved. |
*Each row must provide either userId or email.
Return Type
Section titled “Return Type”type AdminBulkEnrollAttendeesToEventPayload { results: [AdminEventAttendeeBulkResult!] allSucceeded: Boolean}
type AdminEventAttendeeBulkResult { attendee: AdminEventAttendee # null when the row failed wasNewlyCreated: Boolean # false on idempotent re-enroll errors: [AdminBulkRowError!] # null when the row succeeded}
type AdminBulkRowError { code: String! # Stable error code; switch on this in client code. message: String! # Human-readable message (locale follows the school default).}Switching on .code lets you classify per-row outcomes deterministically:
for (const [i, row] of results.entries()) { if (row.attendee) continue; const { code } = row.errors![0]; if (code === "EVENT-003") retryQueue.push(inputs[i]); // transient else if (code === "EVENT-011") atomicRollbackCount++; // atomic mode rollback else if (code.startsWith("ENROLLMENT-")) skip(inputs[i], code); // input issue, skip row else alert("unexpected", code);}results[i] always corresponds to inputs[i] — order is preserved.
AdminEventAttendee in each row exposes id (Enrollment id), userId, paymentId, email, name, attendStatus, checkInAt, and createdAt. See Enroll Attendee → AdminEventAttendee Object for full field details. Pass attendee.id (or attendee.userId / attendee.email) to removeAttendeeFromEvent if you need to revert a row.
Behavior
Section titled “Behavior”atomic: false (default)
Section titled “atomic: false (default)”Each row is processed independently. A failure on one row does not affect other rows. Per-row errors appear in results[i].errors.
Dedup scope
Section titled “Dedup scope”EVENT-008 only rejects duplicates within the same batch (two rows in inputs pointing at the same user). Rows that point at users already enrolled in the event are accepted and go through the idempotent path — wasNewlyCreated: false, no new Payment is created.
atomic: true
Section titled “atomic: true”All rows are wrapped in a single transaction. The first row failure triggers a rollback of every row in the batch. After rollback:
- The failing row’s
results[i].errors[0]carries the original{code, message}. - Every other row’s
results[i].errors[0]carries{code: "EVENT-011", message: "Rolled back due to row N failure"}. Filter on this code to count rollback losses for monitoring. - No enrollments or payments are persisted.
Atomic mode caveat: side effects from
deliver_latermailers or already-enqueued background jobs cannot be unwound on rollback. Use atomic mode for “all or nothing” data integrity, not for guaranteeing no notifications fire.
Example
Section titled “Example”mutation BulkEnroll { bulkEnrollAttendeesToEvent( eventId: "evt_01HQ..." atomic: false inputs: [ { userId: "usr_01ABC..." } { email: "newuser@example.com", name: "New User" } ] ) { results { attendee { id userId email } wasNewlyCreated errors { code message } } allSucceeded }}Common Errors (top-level)
Section titled “Common Errors (top-level)”| Code | Description |
|---|---|
EVENT-001 | Event not found |
EVENT-002 | Ticket not found or does not belong to this event |
EVENT-004 | Event is canceled or completed |
EVENT-005 | Event has no ticket configured |
EVENT-007 | Batch size invalid (inputs empty, or > 200 rows) |
EVENT-008 | Duplicate userId or email within the batch |
Per-row errors (in results[i].errors) draw from the same set as enrollAttendeeToEvent, plus:
| Code | Where it appears | Description |
|---|---|---|
EVENT-011 | atomic mode rollback rows | Row was rolled back because an earlier row failed; the failing row’s own code is unaffected. |
GENERAL-001 | per-row rescue | Unexpected error during this row. Treat as transient and surface to alerting; do not silently retry without inspection. |
OAuth Scope
Section titled “OAuth Scope”Requires students:write.
Related Resources
Section titled “Related Resources”For more information about the Teachify Admin API, please refer to the API Overview.