Events

List and publish events, register participants, and manage registrations.

List events

Shell
curl "https://api.loupe.ch/api/public/cms-events?organization.slug=riveville&locale=en" \  -H "Accept: application/json"

Each event carries what you need to display it:

JSON
{  "id": "0199…",  "title": "Creative writing workshop",  "slug": "creative-writing-workshop",  "cover": { … },  "startAt": "2026-10-17 14:00",  "endAt": "2026-10-17 16:00",  "locationType": "address",  "locationName": "Agora, level 1",  "locationAddress": "…",  "onlineUrl": null,  "categories": [ { "id": "0199…", "name": "Workshops" } ],  "enableRegistration": true,  "capacity": 12,  "membersOnly": false,  "content": "<p>…</p>"}

startAt and endAt are local times in the library's time zone. locationType is address, map (a place on the library's map), free (free text in locationName) or none; online events also carry an onlineUrl.

Filter by category with categories.id[]=<uuid>, or by title with title=. Events come most recently published first; sort them by startAt on your side for an agenda. The public list only contains published events: a cancelled event is unpublished, so it disappears from it.

Categories come from GET /api/public/cms-event-categories?organization.slug=riveville.

Show one event

Shell
curl "https://api.loupe.ch/api/public/cms-events/item?organization.slug=riveville&slug=creative-writing-workshop"

Register a participant

When enableRegistration and enableEmailRegistration are both true, anyone can register with their contact details:

Shell
curl -X POST https://api.loupe.ch/api/public/cms-events/0199…/register \  -H "Content-Type: application/json" \  -d '{    "email": "camille@example.org",    "firstname": "Camille",    "lastname": "Martin",    "guests": 1,    "comment": "Wheelchair access needed"  }'

The API answers 200 with a token. The participant receives Loupe's confirmation e-mail, with a link to manage or cancel the registration.

guests counts the people coming with the participant; it cannot exceed the event's capacityGuests, which is 0 unless the library allows companions. Each registration takes 1 + guests seats out of capacity.

Call this endpoint without an API key: when a key is present, the registration is made in the name of the key's owner, not of the person in the body.

To make retries safe, send a confirmationId (a UUID you generate). Replaying the same request returns the original registration instead of creating a second one.

StatusdetailMeaning
400Registration disabled for this event.Registrations are closed
400Event does not allow simple email registration…Only signed-in readers can register
400Guests exceed the allowed capacity…guests is above capacityGuests
400You are already registered for this event.Same e-mail, active registration
400too_many_registrationFull; context.remaining says how many seats are left
400members_onlyReserved for library members, see context.reason
404Unknown or unpublished event

Manage events

These endpoints need an API key whose owner holds the matching permission: events.create, events.update_own or events.update_any, and so on. "Own" covers the events that person created.

Create an event. It starts as a draft:

Shell
curl -X POST "https://api.loupe.ch/api/manager/cms-events?organization.slug=riveville&locale=en" \  -H "X-Api-Key: $LOUPE_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "title": "Creative writing workshop",    "slug": "creative-writing-workshop",    "startAt": "2026-10-17 14:00",    "endAt": "2026-10-17 16:00",    "locationType": "free",    "locationName": "Agora, level 1",    "enableRegistration": true,    "enableEmailRegistration": true,    "capacity": 12,    "capacityGuests": 2,    "categories": ["/api/manager/cms-event-categories/0199…"]  }'

title and slug are required; the slug must be unique within the organization. Dates use the local YYYY-MM-DD HH:mm format. A capacity of 0 or null means unlimited. locale chooses the language the title and content are written in; send the same request with another locale to add a translation.

Publish or update with a merge patch. Publishing is a field, not a separate call, and needs events.publish_own or events.publish_any on top of the update permission. Set publishedAt at the same time, since public lists are ordered by it:

Shell
curl -X PATCH https://api.loupe.ch/api/manager/cms-events/0199… \  -H "X-Api-Key: $LOUPE_API_KEY" \  -H "Content-Type: application/merge-patch+json" \  -d '{ "isPublished": true, "publishedAt": "2026-10-09", "capacity": 15 }'

Changes are live immediately, and each one is kept in the event's version history in the Loupe admin.

Cancel with POST /api/manager/cms-events/{id}/cancel and {"reason": "…"}. The event is unpublished, registrations close, and every participant is e-mailed the reason.

Write to participants with POST /api/manager/cms-events/{id}/notify-participants and {"subject": "…", "body": "…"} (up to 3,000 characters): each active participant receives the message by e-mail.

Duplicate with POST /api/manager/cms-events/{id}/duplicate (optional {"title": "…"}). The copy is a draft with its own slug and no registrations; it keeps the content, dates, cover, categories and registration settings.

Delete with DELETE /api/manager/cms-events/{id}. The event goes to the Loupe trash for 30 days. An event that still has registrations answers 409: cancel it first.

Registrations

MethodPathUse
GET/api/manager/cms-event-registrations?event.id=<uuid>Registrations of an event
POST/api/manager/cms-event-registrations/{id}/cancelCancel one registration with {"reason": "…"}; the participant is e-mailed
GET/api/manager/statistics/cms-events?from=…&to=…Events, registrations and attendance for a period

These need event_registrations.own or event_registrations.any. Prefer cancelling to deleting a registration: deletion sends no e-mail and leaves no trace.