Registrations
A registration is one person signing up for one event.
Archived registrations are not returned. Archiving is how the Novex UI removes a
registration, so an archived row disappears from both GET /registrations and
GET /registrations/{id} — the detail route returns 404. There is no way to read an
archived registration through the API.
GET /registrations
curl -sS "https://nov3x.com/api/public/v1/registrations?eventId=clx1234567890" \
-H "Authorization: Bearer nvx_live_your_key_here"Scope: registration.read
Query parameters
| Parameter | Type | Notes |
|---|---|---|
limit | integer | Page size, 1–100. Defaults to 25. |
cursor | string | From the previous response’s meta.cursor. |
updatedSince | ISO 8601 | Only registrations updated at or after this instant. |
eventId | string | Restrict to one event. |
status | string | Filter by registration status. |
POST /registrations
Create a registration for an event. This is the same path the public registration form takes, so capacity, waitlist, plan limits, duplicate detection and the organiser’s own custom fields all behave exactly as they do on the form — because it is the same code underneath, not a second implementation.
curl -sS -X POST "https://nov3x.com/api/public/v1/registrations" \
-H "Authorization: Bearer nvx_live_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"eventId": "clx1234567890",
"name": "Aisha Rahman",
"email": "aisha@acme.example",
"company": "Acme"
}'Scope: registration.write · Rate limit: 120/min (the write ceiling) ·
Idempotency-Key required — see Idempotency.
Body
| Field | Type | Notes |
|---|---|---|
eventId | string | Required. The event to register for. |
name | string | Required. Up to 200 characters. |
email | string | Required. Up to 320 characters. |
formId | string | Optional when the event has exactly one open registration form. Required when it has several — see below. |
phone | string | E.164 preferred. |
salutation | string | Feeds the email greeting. |
company | string | |
jobTitle | string | |
country | string | |
city | string | |
language | string | The attendee’s language, used for their confirmation email. |
ticketTypeId | string | Must belong to this event. |
customFields | object | The organiser’s own form fields, keyed by field id. Required fields are enforced; an unknown key is ignored. |
notify | boolean | Defaults to true. Set false for a bulk import of historical attendees, which must not email everyone. |
Which form?
An event can have more than one registration form (a delegate form and a speaker
form, say). When exactly one is open, you may omit formId and we use it. When
several are open, formId is required and omitting it returns 422 — which form
an attendee belongs to is not something we can guess for you.
Responses
| Status | Meaning |
|---|---|
201 | Created. The body is the registration, in the same shape the GET routes return. |
202 | The event was full and has a waitlist, so the attendee was waitlisted. data.status is WAITLISTED — do not tell them they have a seat. |
404 | No such event in your workspace, or it is archived, cancelled or postponed. |
409 | See the table below — branch on error.code, not on the status. |
422 | The body failed validation, a required custom field is missing, or the event has several forms and you named none. |
409 codes
error.code | What happened |
|---|---|
EVENT_FULL | At capacity with no waitlist configured. |
DUPLICATE_REGISTRATION | This person is already registered for this event. |
PLAN_LIMIT_REACHED | The workspace has hit its plan’s registration limit for this event. |
IDEMPOTENCY_KEY_REUSED | The key was used for a different request body. |
IDEMPOTENCY_REQUEST_IN_PROGRESS | The original request under this key is still running. |
IDEMPOTENCY_RESPONSE_UNAVAILABLE | The original response was too large to store for replay. |
A new Idempotency-Key will not clear the first three. They are properties of
the event or the attendee, not of your request: raise the capacity, enable the
waitlist, or check whether the person is already in.
Duplicates
A registration is refused as a duplicate when the name, email and phone all match an existing active registration for the same event. Several people legitimately share one email address — a company admin registering colleagues, a family — so an email-only match would drop real attendees.
Confirmation email
By default the attendee gets the same confirmation email and QR badge the public
form sends. Pass "notify": false to suppress it.
We do not record marketing consent through this endpoint, and there is no field for it. Consent has to be something the person actually gave; a flag set by an integrator is not that, and it would be indistinguishable from a real one afterwards.
GET /registrations/{id}
Returns 404 if the registration is not in your workspace.
Fields
| Field | Type | Notes |
|---|---|---|
id | string | Stable identifier |
eventId | string | The event this registration belongs to |
status | string | e.g. CONFIRMED |
name | string | Attendee name |
email | string | Attendee email |
checkedIn | boolean | Whether they have been checked in |
checkedInAt | ISO 8601 or null | When, or null if not checked in |
createdAt | ISO 8601 | Created |
updatedAt | ISO 8601 | Last modified — use for sync |
POST /registrations/{id}/check-in
Marks an attendee as arrived. There is no request body — the registration id is the whole input.
Check-in is state on the registration, not a separate resource: it is an
action on a registration, and you read the result back as checkedIn and
checkedInAt. There is no check-in collection to list.
curl -sS -X POST \
"https://nov3x.com/api/public/v1/registrations/clx1234567890/check-in" \
-H "Authorization: Bearer [REDACTED]" \
-H "Idempotency-Key: 8f14e45f-ea0a-4c1b-9a6e-0d1c2b3a4e5f"Scope: registration.checkin — deliberately narrower than
registration.write. A badge or kiosk vendor can check attendees in without
gaining the ability to create registrations.
The Idempotency-Key header is required. A
write without one is refused with 400 IDEMPOTENCY_KEY_REQUIRED.
Returns the registration itself, in exactly the shape GET /registrations/{id}
returns — so you never need a second call to see the result.
Re-scanning is safe
If the attendee is already checked in, this returns 200 with the current
record, not an error, and checkedInAt keeps the instant they actually
arrived. That is deliberate: a scanner at a door re-scans, retries over a flaky
link, and runs two lanes against one queue. Treating a successful outcome as a
conflict would turn a working door into an alerting one.
Check-in is also idempotent in the data itself — the flag only moves one way — so a duplicate scan changes nothing even if the two requests carried different keys.
When it is refused
| Status | Code | When |
|---|---|---|
404 | NOT_FOUND | No such registration in your workspace, or it is archived. |
409 | REGISTRATION_NOT_CHECKABLE | The booking is not CONFIRMED: it is CANCELED, REJECTED, WAITLISTED or NO_SHOW. |
Only a CONFIRMED booking can be checked in. The 409 carries the reason in
error.details.reason — one of canceled, rejected, waitlisted or no_show —
so your scanner can tell the person at the door why.
This is the same rule every Novex check-in uses: the kiosk, the staffed scanner,
the offline kiosk and the registration page all refuse the same bookings. There is
no override flag. To admit someone whose booking is not confirmed — a waitlisted
guest you have a seat for, a no-show who turns up late — change the booking’s
status to CONFIRMED in Novex first, then check them in with a new
Idempotency-Key.
⚠️ A
409 REGISTRATION_NOT_CHECKABLEis remembered against yourIdempotency-Key. So if you confirm the booking and retry with the same key, you get the stored refusal back rather than a fresh attempt. Retry with a new key after correcting the cause. This is also why you should derive the key from the attempt — a fresh UUID per scan — rather than from the registration id: a key derived from the record inherits the record’s history.
What is not exposed
The endpoint records who performed the check-in in your workspace’s audit trail, not on the registration. An API call has no Novex user behind it, so no staff member is named — and the internal staff-user field is not part of any public payload in the first place.
What it does not do
Nothing else. A check-in through the API does exactly what a check-in at the kiosk does: it writes the record and an audit entry. No email, no notification, no webhook — the desk does not send one either, and an integration should not quietly do more than the product does.
⚠️ Badge QR tokens are never returned by this API. A badge token is the entire credential for that badge — anyone holding it could present it and be checked in — so it is not part of any public payload, by design.
What is not included
Attendee phone, company, job title, country and city are stored in
Novex but are not returned by this API in v1. We expose the minimum that
makes the resource useful. If your integration genuinely needs more, tell us —
adding a field is a non-breaking change, so we can do it without a new version.