// docs

API Reference

Every REST v1 endpoint — parameters, auth scope, response shapes, error codes, and rate limits.

API Reference

Base URL: https://www.envpilot.dev/api/v1. All endpoints require the Authorization: Bearer <api key> header and are read-only. Requires the Pro plan (public_api feature). See API Quickstart to create a key and API Security for the auth and audit model.

Every response is JSON unless format=env is passed. Every error response is { "error": string, "code": string } plus an x-request-id header for support.

Endpoints#

GET /v1/organization

Returns metadata for the key's organization. Works with any valid API key of the organization regardless of its resource scope — org metadata is discovery-level information the key holder already has.

terminal
curl https://www.envpilot.dev/api/v1/organization \
  -H "Authorization: Bearer envpk_..."
json
{
  "id": "org_abc123",
  "name": "Acme Inc",
  "slug": "acme",
  "plan": "pro"
}

Auth: any valid key. Rate limit: metadata bucket (120/min).


GET /v1/projects

Lists every project in the key's scope (name, slug, and resource counts). Projects outside scope are silently omitted — an empty scope match returns 200 with an empty list, never an error.

terminal
curl https://www.envpilot.dev/api/v1/projects \
  -H "Authorization: Bearer envpk_..."
json
{
  "projects": [
    {
      "id": "proj_1",
      "name": "Backend",
      "slug": "backend",
      "variableCount": 42,
      "accountCount": 3
    }
  ]
}

Auth: requires projects in the key's resources. Rate limit: metadata bucket (120/min).


GET /v1/projects/{slug}

Returns a single project's metadata.

terminal
curl https://www.envpilot.dev/api/v1/projects/backend \
  -H "Authorization: Bearer envpk_..."

Auth: projects resource, project must be in scope. 404 if the slug doesn't exist or is outside the key's scope — the two cases are indistinguishable on purpose. Rate limit: metadata bucket (120/min).


GET /v1/projects/{slug}/variables

The workhorse endpoint. Pulls decrypted variable values for one environment, or metadata-only if you don't need values.

Query paramRequiredDescription
environmentYes, unless metadata_onlydevelopment, staging, or production
keysNoComma-separated exact key names, e.g. keys=A,B,C
prefixNoOnly keys starting with this prefix, e.g. prefix=NEXT_PUBLIC_
metadata_onlyNotrue returns key names only — no vault decrypt, no values
formatNojson (default) or env (dotenv text output)
terminal
curl "https://www.envpilot.dev/api/v1/projects/backend/variables?environment=production&prefix=NEXT_PUBLIC_" \
  -H "Authorization: Bearer envpk_..."
json
{
  "environment": "production",
  "variables": [
    {
      "key": "NEXT_PUBLIC_API_URL",
      "value": "https://api.acme.com",
      "updatedAt": 1752192000000
    }
  ]
}

Auth: variables resource, project + environment must be in scope. Rate limit: metadata reads (metadata_only=true) count against the 120/min metadata bucket; value pulls count against the 30/min value bucket. 422 if the project has more than 1000 matching variables — the API refuses to return a partial list rather than silently truncating. 503 if vault decryption fails for any variable in the response — the whole request aborts rather than returning a partial or sentinel value.


GET /v1/projects/{slug}/accounts

Lists shared accounts (credentials shared with the team, distinct from variables) for a project.

Query paramRequiredDescription
environmentNoFilter to one environment
metadata_onlyNotrue returns account names only, no secret values
terminal
curl "https://www.envpilot.dev/api/v1/projects/backend/accounts?environment=production" \
  -H "Authorization: Bearer envpk_..."

Auth: accounts resource, project must be in scope. Rate limit: metadata reads use the 120/min bucket; value pulls use the 30/min bucket.


GET /api/v1/secrets

Unchanged legacy endpoint used by the GitHub Action — pulls variables for a single project + environment service token. Kept for backward compatibility; new integrations should use GET /v1/projects/{slug}/variables instead.

Errors#

Every non-2xx response is { "error": string, "code": string }.

StatusWhen it happens
401Key is missing, malformed, invalid, expired, or revoked — one uniform message so revoked/invalid/expired can't be distinguished by an attacker
403Key is valid but its scope or plan doesn't include this resource/feature (e.g. calling /accounts with a variables-only key, or public_api not enabled for the org's plan)
404Project slug doesn't exist, or exists but is outside the key's scope — both return the same 404 so scope can't be probed by existence
422The response would exceed 1000 variables — refused outright rather than returning a partial/truncated list
429Rate limit exceeded — carries a Retry-After header (seconds until the bucket refills)
503Vault decrypt failed for one or more variables mid-request — the whole request aborts, never a partial result with missing values

Rate limits#

Rate limits are per API key, enforced with a token-bucket limiter.

BucketLimitApplies to
Metadata reads120 / min/organization, /projects, /projects/{slug}, and any request with metadata_only=true
Value pulls30 / min/projects/{slug}/variables and /projects/{slug}/accounts when returning decrypted values

A 429 response includes a Retry-After header with the number of seconds to wait. Requests using an unrecognized key hash are rate-limited separately (per hash) to slow down brute-force probing.

CORS#

The API does not send CORS headers and is not intended to be called from a browser — API keys must never be embedded in client-side code. See API Security.