Public Endpoints
These endpoints do not use a normal user session. Each one is protected in a different way: a secret in the URL, a static bearer token, a kiosk PIN, or nothing at all for non-sensitive health data. Booking webhooks are covered separately in Booking Webhooks, and the BI feed in BI Feed.
Employee Calendar Feed (ICS)
Employees can subscribe to their own published shifts from Google Calendar, Apple Calendar or Outlook. They turn the feed on from their profile page.
Self-service management (authenticated)
| Route | Purpose |
|---|---|
GET /api/v1/calendar-feed | Current status: { enabled, url } |
POST /api/v1/calendar-feed/enable | Turns the feed on (idempotent) and returns the URL |
POST /api/v1/calendar-feed/regenerate | Issues a new secret URL. The old URL stops working immediately. |
These routes identify the employee from the session, so a caller can only manage their own feed. The feed is off (no token) until the employee turns it on.
Subscription URL (public)
GET /api/calendar/<token>.ics
- Version-neutral route with no session. The secret token in the URL is the only credential, so treat the URL like a password.
- An unknown or revoked token returns
404. The response never reveals whether a token once existed. - Rate limited to 60 requests per minute. Responses are sent as
text/calendarwith a 5-minute private cache. - The feed contains only the employee's own published shifts, from 90 days in the past onward. Shifts cancelled in the last 30 days are included with
STATUS:CANCELLEDso that calendar apps remove them. - Each event has a stable
UIDper shift and aSEQUENCEtaken from the shift's version, so edits update the existing event. Times useTZID=Europe/Helsinki.
Calendar apps poll on their own schedule, and Google Calendar in particular can take several hours to show changes.
Public Usage Statistics
GET /api/public-stats
Authorization: Bearer <PUBLIC_STATS_TOKEN>
This is an aggregate, cross-tenant usage summary that the marketing site loads at build time. The route is only registered when PUBLIC_STATS_TOKEN (at least 16 characters) is configured. It is rate limited to 10 requests per minute.
The response contains only non-negative integer counts plus a timestamp:
shiftsScheduled, hoursTracked, clockIns, timesheetsProcessed, payrollRuns, activeEmployees, activeLocations, complianceViolationsFlagged, shiftPulses, satisfactionResponses, satisfactionRatingAvgX10, netvisorExports, bookingsSynced, leaveRequestsHandled, generatedAt.
satisfactionRatingAvgX10is the average in-app satisfaction rating multiplied by 10. Divide by 10 for display (for example43→ 4.3 / 5).- Demo and seed tenants are excluded. No euro amounts and no tenant-identifying data are returned.
- The queries take no request input, and the endpoint only ever returns this fixed aggregate object.
Health Checks
All health routes are public and version-neutral.
| Route | Response |
|---|---|
GET /api/health | Liveness: { status: "ok", version: { sha, buildTime }, database: { pool } }. Deploys use version.sha to confirm that the new build is live. |
GET /api/health/ready | Readiness: checks the Postgres and Redis connections |
GET /api/health/backup | Backup freshness. Returns 200 { status: "ok", lastSuccess } if the last successful off-site backup finished within 26 hours, otherwise 503 { status: "stale", lastSuccess }. Point external uptime monitoring at this route. |
Kiosk
Shared kiosk devices call POST /api/v1/kiosk/identify, clock-in, clock-out, break-start, break-end and pulse without a user session. Each request carries the tenant, location and the employee's PIN (plus a device ID for clock actions). The routes are rate limited to 10 requests per minute. See Kiosk Mode for the user-facing setup.
Related Authenticated Endpoints
In-app satisfaction survey:
GET /api/v1/satisfaction-survey/statusreturns{ eligible }. A user is eligible if they have not rated in the last 90 days.POST /api/v1/satisfaction-surveysubmits a 1–5 rating. It returns409inside the 90-day cooldown.GET /api/v1/satisfaction-survey/summary(management roles) returns the average, distribution, count and monthly trend.
The web app shows the prompt only to employees, and only to a random sample. The sample rate is set with NEXT_PUBLIC_SATISFACTION_SURVEY_SAMPLE_RATE (default 0.15; 0 turns the prompt off).