Skip to main content

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)​

RoutePurpose
GET /api/v1/calendar-feedCurrent status: { enabled, url }
POST /api/v1/calendar-feed/enableTurns the feed on (idempotent) and returns the URL
POST /api/v1/calendar-feed/regenerateIssues 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/calendar with 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:CANCELLED so that calendar apps remove them.
  • Each event has a stable UID per shift and a SEQUENCE taken from the shift's version, so edits update the existing event. Times use TZID=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.

  • satisfactionRatingAvgX10 is the average in-app satisfaction rating multiplied by 10. Divide by 10 for display (for example 43 → 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.

RouteResponse
GET /api/healthLiveness: { status: "ok", version: { sha, buildTime }, database: { pool } }. Deploys use version.sha to confirm that the new build is live.
GET /api/health/readyReadiness: checks the Postgres and Redis connections
GET /api/health/backupBackup 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.

In-app satisfaction survey:

  • GET /api/v1/satisfaction-survey/status returns { eligible }. A user is eligible if they have not rated in the last 90 days.
  • POST /api/v1/satisfaction-survey submits a 1–5 rating. It returns 409 inside 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).