Skip to content

Authentication

PayWarden has two distinct auth surfaces:

SurfaceUsed byMechanism
Merchant APIYour server-side integrationX-API-Key header
Admin APIDashboard / operatorshttpOnly cookie admin_token

Merchant API key authentication

All merchant API endpoints require an X-API-Key header. The following endpoints are public (no auth):

  • GET /api/v1/health
  • GET /api/v1/health/live
  • GET /api/v1/checkout/:id/status
bash
curl https://pay.yourdomain.com/api/v1/payments \
  -H "X-API-Key: pw_live_abc123..."

Each merchant has their own API key. The key is stored in the database as an HMAC-SHA256 hash — the raw key is shown exactly once at creation time and cannot be retrieved again. If a key is lost, rotate it via the admin API.

Keys are prefixed (e.g. pw_live_xxxx) so they can be indexed in logs without exposing the secret portion.

Generating API keys

Via admin dashboard

  1. Log in at /admin
  2. Go to Merchants
  3. Click Create Merchant
  4. Copy the generated API key — it is shown only once

Via admin API

Admin endpoints are authenticated by the admin_token httpOnly cookie set on login. Use curl -c to save the cookie and -b to reuse it:

bash
# 1. Log in — stores the admin_token cookie in cookies.txt
curl -X POST https://pay.yourdomain.com/api/v1/admin/auth/login \
  -c cookies.txt \
  -H "Content-Type: application/json" \
  -d '{"password": "your-admin-password"}'

# 2. Create a merchant using the saved cookie
curl -X POST https://pay.yourdomain.com/api/v1/admin/merchants \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Shop",
    "webhook_secret": "a-long-random-string"
  }'

Response:

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "My Shop",
  "api_key": "pw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "api_key_prefix": "pw_live_xxxx",
  "webhook_secret": "a-long-random-string",
  "is_active": true,
  "created_at": "2026-01-01T00:00:00.000Z"
}

Save the API key now

The full api_key is returned only on creation. Store it securely. If lost, rotate via POST /api/v1/admin/merchants/:id/rotate-api-key.

Admin authentication

The admin dashboard uses an httpOnly JWT cookie. There is no Bearer token support — all admin requests must carry the admin_token cookie.

bash
# Login — rate-limited to 5 attempts per 15 minutes
curl -X POST https://pay.yourdomain.com/api/v1/admin/auth/login \
  -c cookies.txt \
  -H "Content-Type: application/json" \
  -d '{"password": "your-admin-password"}'

# Logout
curl -X POST https://pay.yourdomain.com/api/v1/admin/auth/logout \
  -b cookies.txt

Cookie properties: httpOnly, SameSite=Strict, Secure in production, path /, 24-hour max age.

Admin password is set via ADMIN_PASSWORD in .env.

See Admin API for the full admin endpoint reference.

Error responses

Errors follow the shape { error, code }:

json
{
  "error": "Missing API key",
  "code": "UNAUTHORIZED"
}
StatusCodeMeaning
401UNAUTHORIZEDMissing or invalid X-API-Key
403MERCHANT_INACTIVEValid key but merchant is suspended
401ADMIN_UNAUTHORIZEDAdmin endpoint hit without admin_token cookie
401ADMIN_TOKEN_INVALIDadmin_token cookie present but JWT invalid or expired
401INVALID_PASSWORDWrong password on admin login

Released under the BSL 1.1 License.