Docs/ API Reference/ API Authentication

API Authentication

Authenticate with the sh0 REST API using JWT Bearer tokens or API keys for programmatic access.

Getting an API Token

To get a JWT token, send a POST request to the login endpoint with your credentials:

Terminal
curl -X POST https://your-server:9000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "your-password"
  }'

If the credentials are valid, the API returns a JWT token and user information:

Response (200 OK)
{
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfYWJjMTIzIiwiZXhwIjoxNzE...",
    "user": {
      "id": "usr_abc123",
      "email": "[email protected]",
      "name": "Admin",
      "role": "admin"
    }
  }
}

If two-factor authentication is enabled, include the TOTP code:

Terminal
curl -X POST https://your-server:9000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "your-password",
    "totp_code": "123456"
  }'
API login flow diagram showing credential exchange for JWT token

Bearer Token Authentication

Include the JWT token in the Authorization header of every API request:

Terminal
curl https://your-server:9000/api/apps \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

The token contains the user ID, role, and expiration time. sh0 validates the signature and expiry on every request.

Tip
Store the token securely. In scripts, use environment variables rather than hardcoding tokens:
Terminal
export SH0_TOKEN="eyJhbGciOiJIUzI1NiIs..."

curl https://your-server:9000/api/apps \
  -H "Authorization: Bearer $SH0_TOKEN"

API Key Alternative

For long-running integrations (CI/CD pipelines, monitoring scripts, webhooks), API keys are more practical than JWT tokens because they do not expire.

Creating an API key:

  1. Navigate to Settings → API Keys in the dashboard.
  2. Click Generate API Key.
  3. Enter a descriptive name (e.g., "CI/CD Pipeline" or "Monitoring").
  4. Copy the key immediately -- it is shown only once.
API key management panel with generated keys

Using an API key:

Terminal
curl https://your-server:9000/api/apps \
  -H "X-API-Key: sh0_key_abc123def456"
Warning
API keys have the same permissions as the user who created them. Treat them like passwords -- never commit them to version control or share them in plain text.

Token Expiry & Refresh

JWT tokens are valid for 30 days by default. The expiration time is encoded in the token payload.

To refresh an expiring token, call the refresh endpoint with your current (still valid) token:

Terminal
curl -X POST https://your-server:9000/api/auth/refresh \
  -H "Authorization: Bearer YOUR_CURRENT_TOKEN"
Response (200 OK)
{
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs...(new token)...",
    "expires_at": "2026-04-20T12:00:00Z"
  }
}

The dashboard automatically refreshes tokens before they expire. For API integrations, implement token refresh logic or use API keys to avoid expiry issues.

Note
API keys do not expire. They remain valid until manually revoked from Settings → API Keys.

CSRF Tokens for Dashboard Requests

When making requests from the dashboard (browser), sh0 uses CSRF tokens to prevent cross-site request forgery attacks. This is handled automatically by the dashboard's API client.

If you are building a custom frontend that authenticates via cookies (instead of Bearer tokens), you need to include the CSRF token in your requests:

Terminal
# Get a CSRF token
curl https://your-server:9000/api/auth/csrf-token \
  -H "Cookie: session=..."

# Include it in state-changing requests
curl -X POST https://your-server:9000/api/apps \
  -H "Cookie: session=..." \
  -H "X-CSRF-Token: csrf_token_here" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-app"}'
Tip
Bearer token authentication (via the Authorization header) does not require CSRF tokens. CSRF protection only applies to cookie-based sessions.

Authentication Errors

When authentication fails, the API returns a structured error response:

401 Unauthorized
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired authentication token"
  }
}
403 Forbidden
{
  "error": {
    "code": "FORBIDDEN",
    "message": "You do not have permission to perform this action"
  }
}

Common error scenarios:

StatusCodeCause
401UNAUTHORIZEDMissing, invalid, or expired token/API key
401INVALID_CREDENTIALSWrong email or password on login
401TOTP_REQUIRED2FA is enabled but no TOTP code was provided
403FORBIDDENUser role does not have permission for this action
429RATE_LIMITEDToo many login attempts (5 per minute per IP)
Authentication error response in API explorer