Skip to content

Authentication

Latch uses four auth modes.

Send API keys in the X-API-Key header.

Key typePrefixUse case
Publishablepk_browser access checks, events, and customer auth
Secretsk_server-side subscription lookup, checkout, and portal

Publishable keys are safe in browser code and can call:

  • GET /api/v1/access/check
  • POST /api/v1/events
  • POST /api/v1/auth/customers/register
  • POST /api/v1/auth/customers/login
  • POST /api/v1/auth/customers/refresh
  • POST /api/v1/auth/customers/logout
  • GET /api/v1/auth/customers/me

Secret keys must stay server-side and can call:

  • GET /api/v1/subscriptions/me
  • POST /api/v1/subscriptions/checkout
  • POST /api/v1/subscriptions/cancel
  • POST /api/v1/subscriptions/portal

Customer auth routes accept either publishable or secret keys:

  • POST /api/v1/auth/customers/register
  • POST /api/v1/auth/customers/login
  • POST /api/v1/auth/customers/refresh
  • POST /api/v1/auth/customers/logout
  • GET /api/v1/auth/customers/me

Dashboard management routes use an authenticated admin session cookie.

That includes:

  • products and prices
  • paywalls
  • customers
  • segments
  • API keys
  • stats
  • Stripe settings

POST /api/v1/webhooks/stripe does not use API keys. It validates Stripe signatures instead.

Customer auth is optional. When enabled, customers register and log in with email and password. The API returns:

  • a JWT access token (15-minute expiry) for fast, stateless identity verification
  • an opaque refresh token (30-day expiry) for obtaining new access tokens
  1. Call POST /api/v1/auth/customers/register or /login with any API key (pk_ or sk_)
  2. The API returns accessToken, refreshToken, expiresAt, and customer
  3. Store both tokens on the client (the SDK handles this automatically via localStorage)
  4. Pass the access token as Authorization: Bearer <token> with access checks
  5. When the access token expires, exchange the refresh token for a new pair via /refresh
Terminal window
curl -X POST https://your-api/api/v1/auth/customers/register \
-H "X-API-Key: pk_..." \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "securepass123", "name": "Jane"}'
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "a1b2c3d4e5f6...",
"expiresAt": 1712345678,
"customer": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"name": "Jane"
}
}
Terminal window
curl -X POST https://your-api/api/v1/auth/customers/login \
-H "X-API-Key: pk_..." \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "securepass123"}'
Terminal window
curl https://your-api/api/v1/access/check?url=https://example.com/premium \
-H "X-API-Key: pk_..." \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

When a valid JWT is present, the verified customer ID from the token takes precedence over the userId query parameter.

Refresh tokens are rotated on use. Each call to /refresh invalidates the old refresh token and returns a new pair.

Terminal window
curl -X POST https://your-api/api/v1/auth/customers/refresh \
-H "X-API-Key: pk_..." \
-H "Content-Type: application/json" \
-d '{"refreshToken": "a1b2c3d4e5f6..."}'
Terminal window
curl -X POST https://your-api/api/v1/auth/customers/logout \
-H "X-API-Key: pk_..." \
-H "Content-Type: application/json" \
-d '{"refreshToken": "a1b2c3d4e5f6..."}'

Requests are rate-limited per identity.

Endpoint groupLimit
Access checks1,000 requests/minute
Events200 requests/minute
All other endpoints200 requests/minute

Responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.

The API serves interactive docs at /docs.