API Authentication
Authenticate with the HolyDocs REST API using API keys or Better Auth session tokens.
Overview
The HolyDocs REST API supports two authentication methods: API keys for programmatic access (CLI, CI/CD, server-to-server) and Better Auth session tokens for the dashboard. Every authenticated endpoint accepts either, validated by the same Authorization: Bearer <token> header.
API Key Authentication
API keys are the recommended method for CLI tools, CI/CD pipelines, and server-to-server integrations.
Getting an API Key — Easiest Path
If you have the HolyDocs CLI installed, run:
bashholydocs login
This opens a browser-based device authorization flow — you sign in once and the CLI automatically receives a write-scoped API key (90-day expiry) and stores it in ~/.holydocs/config.json. For CI or headless environments, you can instead use holydocs login --api-key ... or set HOLYDOCS_API_KEY.
Once logged in, you can make authenticated management calls directly from the terminal with holydocs api:
bashholydocs api get /projects
Creating an API Key Manually
For custom scopes, longer expiries, or non-CLI integrations, create keys in the dashboard:
- Go to Settings → API Keys at app.holydocs.com/settings/api-keys
- Click Create API Key
- Enter a name, select the permissions, and optionally set an expiry
- Copy the key — it is only shown once and stored hashed (SHA-256) on the server
API Key Format
API keys use the hd_ prefix followed by a 32-character hex suffix derived from a UUID:
texthd_a1b2c3d4e5f67890a1b2c3d4e5f67890
There are no hd_live_* / hd_test_* environment prefixes — staging and production each have their own dashboards and issue keys against their own databases.
Using API Keys
Include the API key in the Authorization header as a bearer token:
bashcurl -X GET "https://api.holydocs.com/api/v1/projects" \ -H "Authorization: Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890"
This is the only supported transport — there is no X-API-Key header.
If your key doesn't carry an organization context (rare — only happens for legacy keys), you can pass the org explicitly via an X-Organization-Id header alongside the bearer token:
bashcurl -X GET "https://api.holydocs.com/api/v1/projects" \ -H "Authorization: Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890" \ -H "X-Organization-Id: org_abc123"
Permissions
API keys carry a permissions array that's validated at creation time. Common values:
| Permission | Description |
|---|---|
read | Read-only access to projects, deployments, analytics, etc. |
write | Full write access (create/update/delete projects, trigger deployments). Keys issued by holydocs login use this. |
admin | Same as write plus organization-level operations |
Permissions are stored as a JSON array on each key. The CLI's device flow always requests ["write"]. Keys created in the dashboard can specify any combination.
Better Auth Session Tokens
The dashboard and web-based tools use Better Auth for authentication, with session tokens issued via email/password sign-in or social providers (GitHub, Google).
How It Works
- User signs in via the dashboard at
app.holydocs.com/login - Better Auth issues a session token and stores it in an HTTP-only cookie
- The dashboard reads the token client-side and includes it in API requests as
Authorization: Bearer <token> - The API verifies the token by calling Better Auth's
verifyToken()and resolves the user'suserId,orgId, and role
The auth middleware accepts both API keys and session tokens transparently — it inspects the token prefix and routes to the appropriate verifier.
If a session token is valid but doesn't carry an orgId claim, the dashboard sends an X-Organization-Id header as a fallback. The API only honors this header when the token is already verified — it never grants access to an org the user doesn't already belong to.
Base URL
All API requests use the base URL:
texthttps://api.holydocs.com/api/v1
Rate Limiting
API requests are rate-limited per plan:
| Plan | Requests per Minute |
|---|---|
| Free | 60 |
| Starter | 120 |
| Pro | 600 |
| Business | 3,000 |
| Enterprise | 6,000 |
Rate limit headers are included in every response:
textX-RateLimit-Limit: 600X-RateLimit-Remaining: 597X-RateLimit-Reset: 1708200060
Error Responses
All API errors follow a consistent format:
json{ "error": { "code": "AUTH_ERROR", "message": "Invalid or expired API key", "status": 401 }}
Common Error Codes
| Code | Status | Description |
|---|---|---|
AUTH_ERROR | 401 | Missing or invalid authentication |
FORBIDDEN | 403 | Insufficient permissions |
NOT_FOUND | 404 | Resource not found |
VALIDATION_ERROR | 400 | Invalid request body |
ALREADY_EXISTS | 409 | Resource with this identifier already exists |
LIMIT_EXCEEDED | 429 | Rate limit or plan limit exceeded |
Public Endpoints
Some API endpoints do not require an Authorization header:
| Endpoint | Purpose |
|---|---|
GET /api/v1/docs/:projectId/search | Public search API |
GET /api/v1/docs/:projectId/search/semantic | Semantic search |
POST /api/v1/assistant/:projectId/chat | AI assistant chat |
POST /api/v1/mcp/:projectId/message | MCP server |
POST /api/v1/cli/device/code | CLI device authorization — request a new device code |
POST /api/v1/cli/device/poll | CLI device authorization — poll for the issued API key |
POST /api/v1/webhooks/github | GitHub webhooks |
POST /api/v1/webhooks/gitlab | GitLab webhooks |
POST /api/v1/webhooks/stripe | Stripe webhooks |
These endpoints use their own authentication mechanisms (webhook signatures, rate limiting by visitor IP, single-use codes with short TTLs, etc.). Note that POST /api/v1/cli/device/authorize — the third endpoint in the device flow — does require a session token; it's the call the dashboard makes on behalf of a signed-in user to mint the API key.