Space Works Events exposes a tenant-bound, machine-to-machine REST API under /api/v1. It is an application contract, not a generic Prisma API.
Organisation owners manage API clients at /dashboard/:orgSlug/settings/api. Each client belongs to one organisation and has explicit scopes. Credentials are returned once and only their SHA-256 digest is stored.
Send the credential in the standard bearer header:
Authorization: Bearer <credential-shown-at-creation>Store credentials in the consuming service's secret manager. Never put them in URLs, source control, analytics, monitoring tags, or logs. Rotation is zero-downtime: create a replacement credential, deploy it to the consumer, verify it, then revoke the old credential. Credentials can optionally expire, and disabling a client immediately rejects all of its credentials.
Invalid, expired, revoked, and disabled credentials all receive the same generic 401 response.
| Scope | Capability |
|---|---|
events:read |
Read standalone event metadata belonging to the client's organisation |
participants:read |
Read the minimal confirmed-participant roster for an event belonging to the client's organisation; can filter it by attendance without returning attendance data |
participants:attendance:read |
With participants:read, opt in to the checkedInAt timestamp with include=attendance |
orders:read |
Read an event's orders as totals: reference, status, amounts paid and refunded, ticket count |
applications:read |
Count an event's applications (ADMISSION.md) by status and by stage, and the offers out |
participants:attendance:read does not grant roster access by itself. Participant access does not include email, user IDs, registration answers, invitation data, feedback linkage, or ticket/check-in/certificate tokens.
Order access does not include buyer names or email addresses, order links, receipts, card details, or payment provider IDs.
GET /api/v1/events?limit=50&cursor=<cursor>Requires events:read. The response contains explicit event fields and includes draft, published, and hidden standalone events owned by the authenticated organisation.
GET /api/v1/events/:eventIdRequires events:read. Missing and cross-organisation IDs both return the same generic 404 response.
GET /api/v1/events/:eventId/participants?limit=50&cursor=<cursor>&attendance=checked_in&include=attendanceRequires participants:read. Only confirmed standalone-event RSVPs are returned. attendance accepts all (the default), checked_in, or not_checked_in; it does not require an extra scope because it only filters the existing minimal roster. include=attendance returns the nullable checkedInAt timestamp and additionally requires participants:attendance:read.
{
"data": [
{
"id": "example-rsvp-id",
"displayName": "Participant name",
"registeredAt": "2030-01-01T10:00:00.000Z",
"checkedInAt": "2030-01-01T10:15:00.000Z"
}
],
"pagination": {
"nextCursor": null
}
}GET /api/v1/events/:eventId/applicationsRequires applications:read. Counts only: total, byStatus (every application status), stages (position, name, status, how many reached it and how many are in review there) and offers (out: offered plus accepted; limit: the offer capacity, or the event capacity, or null). No applicant names, contact details, answers, reviews, scores or reviewers.
GET /api/v1/events/:eventId/orders?limit=50&cursor=<cursor>Requires orders:read. Returns the standalone event's orders, newest first. Amounts are decimal strings in the order's currency; paid counts settled payments and refunded counts completed refunds.
{
"data": [
{
"id": "example-order-id",
"reference": "SW-7F3K2",
"status": "CONFIRMED",
"currency": "INR",
"total": "1500.00",
"paid": "1500.00",
"refunded": "0.00",
"tickets": 2,
"testMode": false,
"placedAt": "2030-01-01T10:00:00.000Z"
}
],
"pagination": {
"nextCursor": null
}
}Collections use opaque keyset cursors. limit defaults to 50 and cannot exceed 100. Send the returned nextCursor unchanged to fetch the next page. A null cursor means the collection is complete.
Errors have one shape:
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "The requested resource was not found."
}
}Defined codes are INVALID_REQUEST, INVALID_CREDENTIAL, INSUFFICIENT_SCOPE, RESOURCE_NOT_FOUND, RATE_LIMITED, and INTERNAL_ERROR. Rate-limited responses include Retry-After. API responses use Cache-Control: no-store.
- The organisation always comes from the authenticated API client. Caller-supplied organisation identifiers are not accepted.
- Resource queries include the authenticated organisation in the database predicate.
- API routes use explicit selects and validated DTOs; Prisma records are never serialized directly.
- The existing proxy/IP ceiling and a per-client distributed API limit both apply. Production remains fail-closed when Redis is unavailable.
- Normal reads update a throttled last-used timestamp but do not create an audit event per request.
- Client creation, scope changes, credential creation/revocation, and client status changes are audited without secrets or PII.
- Operational health and outbox endpoints retain their separate bearer-secret mechanisms.
Recurring series/instances, RSVP workflow data, participant contact information, registration answers, feedback reads, and machine writes are not part of v1's current contract.