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:

bash
holydocs 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:

bash
holydocs api get /projects

Creating an API Key Manually

For custom scopes, longer expiries, or non-CLI integrations, create keys in the dashboard:

  1. Go to Settings → API Keys at app.holydocs.com/settings/api-keys
  2. Click Create API Key
  3. Enter a name, select the permissions, and optionally set an expiry
  4. 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:

text
hd_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:

bash
curl -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:

bash
curl -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:

PermissionDescription
readRead-only access to projects, deployments, analytics, etc.
writeFull write access (create/update/delete projects, trigger deployments). Keys issued by holydocs login use this.
adminSame 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

  1. User signs in via the dashboard at app.holydocs.com/login
  2. Better Auth issues a session token and stores it in an HTTP-only cookie
  3. The dashboard reads the token client-side and includes it in API requests as Authorization: Bearer <token>
  4. The API verifies the token by calling Better Auth's verifyToken() and resolves the user's userId, 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:

text
https://api.holydocs.com/api/v1

Rate Limiting

API requests are rate-limited per plan:

PlanRequests per Minute
Free60
Starter120
Pro600
Business3,000
Enterprise6,000

Rate limit headers are included in every response:

text
X-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

CodeStatusDescription
AUTH_ERROR401Missing or invalid authentication
FORBIDDEN403Insufficient permissions
NOT_FOUND404Resource not found
VALIDATION_ERROR400Invalid request body
ALREADY_EXISTS409Resource with this identifier already exists
LIMIT_EXCEEDED429Rate limit or plan limit exceeded

Public Endpoints

Some API endpoints do not require an Authorization header:

EndpointPurpose
GET /api/v1/docs/:projectId/searchPublic search API
GET /api/v1/docs/:projectId/search/semanticSemantic search
POST /api/v1/assistant/:projectId/chatAI assistant chat
POST /api/v1/mcp/:projectId/messageMCP server
POST /api/v1/cli/device/codeCLI device authorization — request a new device code
POST /api/v1/cli/device/pollCLI device authorization — poll for the issued API key
POST /api/v1/webhooks/githubGitHub webhooks
POST /api/v1/webhooks/gitlabGitLab webhooks
POST /api/v1/webhooks/stripeStripe 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.

Ask a question... ⌘I