Authentication Guide

Understand the three supported authentication methods: OAuth2 client credentials for server-to-server integrations, Supabase JWT tokens for dashboard users, and API key + public client ID for public routes.

Environment tip: All examples use v3.onsched.com for production. Replace the host with api-stage.onsched.com when calling the staging environment.

When to Use

  • Secure server-to-server integrations that call the API directly.
  • Configure dashboard-powered apps that rely on Supabase-issued JWTs.
  • Enable public-facing booking flows that don't require user authentication.
  • Rotate credentials without interrupting production traffic.

Auth Models

Use CaseHeaders RequiredHow to Obtain
OAuth2 Clients (recommended for API consumers)Authorization: Bearer <access_token>Exchange client_id and client_secret via POST /v3/oauth/token using the client_credentials grant.
Dashboard UsersAuthorization: Bearer <user JWT> + x-api-key: <company key>Retrieve from the v3 dashboard. Tokens map to specific users and expire according to Supabase settings.
Public Routesx-api-key: <company key> + x-client-id: <public client id>Obtain both from the v3 dashboard. Used for /v3/public/* endpoints that don't require user authentication.

Once verified, verifyToken attaches req.user and (when applicable) req.company. verifyCompany then enforces that the supplied x-api-key belongs to the authenticated company unless the request originated from a machine token (client_credentials).

OAuth2 Client Credentials Flow

  1. Generate a client ID/secret pair in the dashboard (POST /v3/clientId, see /api/routes/clientId.js).
  2. Request an access token:
curl -X POST https://v3.onsched.com/v3/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "<client_id>:<client_secret>" \
  -d "grant_type=client_credentials&scope=read write"
  1. Use the access_token as a Bearer token for one hour. Tokens embed company_id, grant_type=client_credentials, and optional scopes.
  2. Rotate secrets periodically; revoke compromised credentials by deleting them via DELETE /v3/clientId/:id.

Machine tokens do not require x-api-key or x-client-id headers—verifyToken resolves the company directly.

Dashboard Token Flow

  1. Authenticate via the v3 dashboard to receive a Supabase JWT.
  2. Include two headers on every API request:
    • Authorization: Bearer <JWT>
    • x-api-key: <Company API key>
  3. The API cross-checks that the JWT user belongs to the company linked to the API key.

Use this flow when you need user-level auditing (for example, when exposing the API directly from your dashboard session). For backend integrations, prefer OAuth2.

Public Routes Flow

  1. Obtain your company's API key and public client ID from the v3 dashboard.
  2. Include both headers on every public API request:
    • x-api-key: <Company API key>
    • x-client-id: <Public client ID>
  3. These credentials authenticate the company but do not identify a specific user. All public routes are scoped to /v3/public/* endpoints.

Use this flow for customer-facing booking widgets, public availability displays, and other scenarios where end users don't have dashboard accounts.

Token Refresh

  • OAuth2 tokens expire after one hour; request a new one with the same client credentials—no refresh token is issued.
  • Dashboard users can call POST /v3/auth/generateRefreshToken to mint a new refresh token when their session is about to expire.

Error Handling

  • 401 Unauthorized: Missing or invalid headers, malformed Basic auth, expired JWT, or invalid public client ID.
  • User not associated: Returned when x-api-key belongs to a company the current dashboard user is not a member of.
  • Invalid Client ID: Returned when the x-client-id header is missing, invalid, or expired (for public routes).
  • Scope violations: If you plan to enforce scopes later, read them from req.scopes (client credentials tokens may include a space-delimited scope string).

Always store secrets in a secure vault and avoid embedding them in client-side code. Use short-lived OAuth2 tokens for any automation or integration work. For public routes, ensure your public client ID is properly configured in the dashboard and hasn't expired.