Skip to Content
APIChangelog

Changelog

Dated changes to /api/public/v1. Read Versioning for what we may change without a version bump and what we may not.

Every entry names the version string reported in X-Novex-API-Version at the time of the change.


2026-10-05 — OTP codes resent once after a short pause on WhatsApp’s side

Version: 2026-08-04-preview

Added — automatic resend: when a live OTP send fails with a temporary WhatsApp error (131042, 131000, 130429, 131016), Novex sends the same code once more about 90 seconds later, if the code still has at least a minute to live. The message reads queued while it waits — a send response can now return queued for this reason. One credit either way; if the resend fails too, the message is failed and the credit comes back once. Other errors are never resent.


2026-10-05 — OTP credit returns for codes never confirmed delivered

Version: 2026-08-04-preview

Added — undelivered status: a live OTP message with no delivered or read report 5 minutes after WhatsApp’s delivery window closes (15 minutes after the send for login, 20 for pickup and delivery) now moves to the final status undelivered, and its credit comes back once. This includes unconfirmed sends, which used to stay charged. A delivery report that arrives later does not change the status or charge again. A client that switches on status should treat undelivered like failed.


2026-10-04 — OTP credit returns when WhatsApp reports a failure

Version: 2026-08-04-preview

Changed — OTP billing: a message that WhatsApp accepted (sent) and later reported failed before delivery now gets its credit back automatically, where it used to keep it. The credit shows in GET /otp/balance once the report arrives; the balance in the send response, and an Idempotency-Key replay of it, still describe the moment of the send. An unconfirmed send stays charged, and a send WhatsApp refuses on the spot is refunded as before.


2026-10-03 — Test keys begin nvx_test_

Version: 2026-08-04-preview

Changed — test keys: a test key created from now on begins nvx_test_. Test keys created earlier begin nvx_live_ and keep working, unchanged. A nvx_test_ key is never accepted as a live key. The 401 that said sandbox keys were reserved is gone: nvx_test_ keys are now issued.


2026-10-01 — OTP message types

Version: 2026-08-04-preview

Added — type on POST /otp/send: login (the default, valid 10 minutes), pickup or delivery (valid 90 minutes). One credit per code whatever the type. Echoed on the send response and on GET /otp/messages/{id}. A send that leaves type out behaves exactly as before.

Added — error code 409 template_pending on POST /otp/send: the type’s WhatsApp message is not available yet; nothing was sent or charged. The test key is never refused this way.


2026-09-30 — OTP messaging

Version: 2026-08-04-preview

Added — OTP messaging: POST /otp/send, POST /otp/verify, GET /otp/messages/{id} and GET /otp/balance, under the new otp.send scope. Send one-time passcodes over WhatsApp against prepaid credits, check them, and read delivery status.

Added — error codes 400 invalid_phone and 402 insufficient_credits on POST /otp/send, and 409 not_verifiable on POST /otp/verify.


2026-09-26 — check-in admits confirmed bookings only

Version: 2026-08-04-preview

Changed — POST /registrations/{id}/check-in now refuses WAITLISTED and NO_SHOW bookings as well as CANCELED and REJECTED, with the same 409 REGISTRATION_NOT_CHECKABLE. Only a CONFIRMED booking can be checked in.

Added — error.details.reason on that 409: canceled, rejected, waitlisted or no_show.

Why. Every Novex check-in now applies one rule. Until this change the other check-in paths refused nothing, so a cancelled attendee’s badge still got them in at a kiosk; now the kiosk, the staffed scanner, the offline kiosk and this endpoint all refuse the same bookings. To admit a waitlisted guest or a late no-show, change the booking to CONFIRMED in Novex first, then retry with a new Idempotency-Key.

Affects you if your integration checks in waitlisted or no-show attendees: those calls now return 409 instead of 200.


2026-09-06 — accreditation decisions

Version: 2026-08-04-preview

Added — GET /registrations/{id}/accreditation. The accreditation decision for one registration, and whether its credential currently stands. Read-only; there is no list endpoint and no write.

Added — scope registration.accreditation.read. A new scope, deliberately not covered by registration.read: a key you already hold gained nothing when this shipped, and never will. Add it in Settings → API to a key that needs it.

The credential code is never returned by any read. It is a capability — whoever holds it can present as the accredited person — so it is shown once, to the organiser, at approval. If you are building a door, read credential.active instead.

Read decision, not status. decision (APPROVED / DECLINED / WITHDRAWN / REVOKED / null) is the outcome and its meaning is stable. status is the raw row state, and a revocation overwrites it to WITHDRAWN — so on status alone a revoked credential and an applicant’s withdrawal look identical. This is why both fields exist.

Nothing changed for existing clients

No existing endpoint, field or error code was modified, and no existing key gained a capability.


2026-09-06 — creating registrations

Version: 2026-08-04-preview

Added — POST /registrations. Create a registration for an event, through the same code path the public registration form uses: capacity, waitlist, plan limits, duplicate detection and the organiser’s custom fields all behave identically. Requires the new registration.write scope and an Idempotency-Key.

Added — the registration.write scope. Existing keys do not gain it. Edit the key in Settings → API to add it; a key that scans badges at a door should keep registration.checkin alone and not be able to invent attendees.

Added — 202 Accepted. A create against a full event that has a waitlist returns 202 with data.status = "WAITLISTED", not 201. If you branch on “2xx means they have a seat”, change that now.

Added — 409 codes EVENT_FULL, DUPLICATE_REGISTRATION and PLAN_LIMIT_REACHED, and a 409 code enum on the create operation in the OpenAPI document. None of them are cleared by retrying with a new Idempotency-Key.

No change to any existing endpoint.


2026-09-05 — the first write: attendee check-in

Version: 2026-08-04-preview

Added — POST /registrations/{id}/check-in. Marks an attendee as arrived. No request body; it returns the registration in the same shape GET /registrations/{id} does, so no second call is needed to see the result.

This is the first write endpoint on the public API. The rules that come with writes are on the new Writes page — an Idempotency-Key is required, writes have their own 120/minute ceiling, and every write is audited.

Added — scope registration.checkin. Narrower than registration.write on purpose: it permits checking attendees in and nothing else. Existing keys are unaffected and do not gain it — add it deliberately in Settings → API.

Added — error code 409 REGISTRATION_NOT_CHECKABLE. Returned when the booking is CANCELED or REJECTED. Unlike the other codes this one describes the record’s state, not your request, so the same call becomes valid once the booking is reinstated — retry with a new Idempotency-Key, because the refusal is remembered against the old one. See Errors.

Documented — the write rate ceiling. 120 requests/minute per key, against 600/minute for reads. One ceiling on every plan that includes API access; this was the ceiling in force from the endpoint’s first day, now written down.

Nothing changed for existing clients

No read endpoint, field, or error code was modified. A client built before this date behaves exactly as it did — the only way to reach the new endpoint is to add the new scope to a key and call it.

Last updated on