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:
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:
{
"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:
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"
}'Bearer Token Authentication
Include the JWT token in the Authorization header of every API request:
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.
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:
- Navigate to Settings → API Keys in the dashboard.
- Click Generate API Key.
- Enter a descriptive name (e.g., "CI/CD Pipeline" or "Monitoring").
- Copy the key immediately -- it is shown only once.
Using an API key:
curl https://your-server:9000/api/apps \
-H "X-API-Key: sh0_key_abc123def456"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:
curl -X POST https://your-server:9000/api/auth/refresh \
-H "Authorization: Bearer YOUR_CURRENT_TOKEN"{
"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.
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:
# 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"}'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:
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired authentication token"
}
}{
"error": {
"code": "FORBIDDEN",
"message": "You do not have permission to perform this action"
}
}Common error scenarios:
| Status | Code | Cause |
|---|---|---|
| 401 | UNAUTHORIZED | Missing, invalid, or expired token/API key |
| 401 | INVALID_CREDENTIALS | Wrong email or password on login |
| 401 | TOTP_REQUIRED | 2FA is enabled but no TOTP code was provided |
| 403 | FORBIDDEN | User role does not have permission for this action |
| 429 | RATE_LIMITED | Too many login attempts (5 per minute per IP) |