API v1 foundation

External API integration

Serveon's external API starts under /api/v1. Tenant identity is resolved from the API key, and every response uses a predictable JSON envelope so integrations can handle success and failure consistently.

The currently implemented public endpoint is GET /api/v1/health. The documented scopes for menu, orders, tables, service requests, and notifications are reserved for upcoming endpoint releases.

Base path

/api/v1

Current endpoint

GET /health

Auth modes

live and test keys

Authentication

Send the raw API key on every request. The API accepts either an authorization bearer token or an X-API-Key header.

Key format
sk_live_<keyId>_<secret>
sk_test_<keyId>_<secret>
Request headers
Authorization: Bearer sk_test_<keyId>_<secret>

# or
X-API-Key: sk_test_<keyId>_<secret>

API key record

Keys are stored in the top-level Firestore apiKeys collection. Raw secrets are shown once and are never stored.

Firestore shape
apiKeys/{keyId}
  tenantId: string
  name: string
  secretHash: string
  secretPreview: string
  scopes: string[]
  enabled: boolean
  allowedOrigins: string[]
  expiresAt: Timestamp | null
  createdAt: Timestamp
  createdBy: string
  revokedAt: Timestamp | null

Secret hashing

New secrets use hmac-sha256: hashes generated with API_KEY_PEPPER. Keep that environment variable stable because existing key hashes depend on it.

The authenticator also accepts legacy unprefixed HMAC andsha256: hashes so older records can be migrated without an immediate outage.

Scopes

ScopePurposeStatus
menu:readRead menu dataReserved
orders:readRead ordersReserved
orders:writeCreate ordersReserved
orders:update_statusUpdate order lifecycle statusReserved
tables:readRead table metadataReserved
service_requests:readRead service requestsReserved
service_requests:writeCreate service requestsReserved
notifications:sendSend notificationsReserved

Health check

Use the health endpoint to validate API key format, secret hash, tenant resolution, mode, audit logging, CORS headers, and rate-limit headers.

Method

GET

Path

/api/v1/health

Required scope

none

Rate limit

60/min/key

Request
curl -H "Authorization: Bearer sk_test_<keyId>_<secret>" \
  http://localhost:3000/api/v1/health
Success response
{
  "success": true,
  "data": {
    "ok": true,
    "tenantId": "tenant-id",
    "mode": "test",
    "keyId": "key-id"
  }
}

Responses and limits

Success envelope
{
  "success": true,
  "data": {}
}
Error envelope
{
  "success": false,
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key"
  }
}

Rate-limit metadata

Successful and rate-limited responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. Reset is a Unix timestamp in seconds.

CORS and browser use

Server-to-server integrations do not need CORS. The API helper supports exact-origin allowlists from each API key record and returns Vary: Origin with the allowed methods and headers.

Current browser integrations should be treated as restricted because authenticated requests use non-simple headers and preflight requests cannot prove tenant identity. Prefer server-side calls until a browser token flow is added.
Allowed request headers
Authorization
Content-Type
Idempotency-Key
X-API-Key
X-Request-ID

Setup checklist

  1. 1Set `API_KEY_PEPPER` and keep it stable across deploys.
  2. 2Create an `apiKeys/{keyId}` document with a hashed secret and tenant ID.
  3. 3Grant only the scopes the integration needs.
  4. 4Use a test key first: `sk_test_<keyId>_<secret>`.
  5. 5Call `/api/v1/health` and confirm `tenantId`, `mode`, and `keyId`.
  6. 6Monitor `apiRequestLogs` and `apiRateLimits` after rollout.