#Auth & API Gateway
Every Helm API call goes through one gateway. It authenticates the caller, applies rate limits, and routes the request to the surface that owns it.
#Base URLs
| Environment | URL |
|---|---|
| Production | https://api.helm.bridge-labs.com |
| Staging | https://api.staging.bridge-labs-dev.com |
The interactive console on this site (the Try it buttons and the API reference) targets staging by default, so experimenting never touches production.
#Authentication
Authenticate with a Personal Access Token using HTTP Basic auth — your
client_id and client_secret encoded as base64(client_id:client_secret) in the
Authorization header. To create a token, see Provisioning.
Enter your token once in the Authorize bar at the top of this page, then Send the call below — it returns the actions your token is allowed to perform, which is the quickest way to confirm your credentials work:
GET/access/me/permissionsWho am I — verify the tokenAPI docs ↗Try it
A 401 means the token is missing, malformed, expired, or revoked.
#Routes
The gateway routes by path prefix:
| Prefix | What lives here |
|---|---|
/entity/* |
Entity types, entities, relationships, query, GraphQL, artifacts |
/schema/* |
JSON schemas and validation |
/definition/* |
Workflow, job, and app-spec definitions, plus owner shares |
/workflows/* |
Workflow activation, triggering, and run monitoring |
/apps/* |
App activation (config, integrations, tokens) and render |
/access/* |
Users, tokens, roles, policies, grants, shares, manifests, secrets |
/event/* |
Event publishing and processing |
GET /healthz is unauthenticated and reports gateway health.
#Rate limits
Production applies two independent limits, each over a rolling 60-second window:
| Limit | Ceiling | Applied | Keyed on |
|---|---|---|---|
| Per-IP | 10,000 req / 60 s | before auth | the originating client IP |
| Per-identity | 1,000 req / 60 s | after auth | the authenticated identity (see below) |
A request must satisfy both. The per-IP ceiling is deliberately generous so a shared egress (an office or VPN where many users share one IP) isn't throttled as a group; in normal authenticated use you reach the per-identity 1,000/60 s limit first. "Identity" is whatever credential you present, and each gets its own independent budget:
- a signed-in user (per user);
- an API token / machine client (per client);
- an activated app rendering for one of its end users (per end user, per integration) — so one busy end user can't exhaust every other end user of the same app;
- an inbound webhook (per trigger).
Because the windows are rolling, capacity recovers continuously as your older requests age out of the trailing 60 seconds — there's no clock-aligned reset to wait for.
When you exceed a limit the API returns HTTP 429:
{ "error": { "status": 429, "code": "TOO_MANY_REQUESTS", "message": "Too many requests. Please try again later.", "details": { "scope": "identity", "limit": 1000 } } }
details.scope is "ip" or "identity" (which limit you hit) and details.limit
echoes that ceiling; no identifying data is included. There is no Retry-After
header — back off (exponential, with jitter) and retry. On successful responses
the API returns X-Rate-Limit-Limit and X-Rate-Limit-Remaining so you can pace
yourself; these are not present on a 429. To stay under the limits: batch where the
API supports it, cache reusable responses, and spread bulk work over time rather than
bursting.
#Responses
Every success body wraps the resource in data:
// single resource (200/201)
{ "data": { "id": "…", "name": "…" } }
// list (200) — pagination is a SIBLING of data, never inside it
{ "data": [ … ], "pagination": { "page": 1, "limit": 20, "total": 42, "total_pages": 3 } }
Keyset-paginated endpoints use next_cursor / prev_cursor siblings instead of
pagination. Token-mint endpoints return the raw secret once as a sibling of
data — capture it then; it is never retrievable again.
#Errors
Every error uses one envelope:
{ "error": { "status": 422, "code": "VALIDATION_ERROR", "message": "Human-readable explanation.", "details": {} } }
The HTTP status is authoritative; code is a stable machine-readable string and
details is code-specific (validation errors carry the offending JSON path; conflicts
carry the conflicting id). Common codes: 400 BAD_REQUEST, 401 UNAUTHORIZED,
403 FORBIDDEN, 404 NOT_FOUND, 409 CONFLICT, 422 VALIDATION_ERROR,
429 TOO_MANY_REQUESTS.
#Conventions
- Casing — request/response fields are
snake_case; the onlycamelCasesurface is an app spec'sspec_document(the authored definition), which the platform translates at the boundary. - Methods —
GETreads,POSTcreates (201),PUTreplaces or mints,PATCHpartially updates,DELETEremoves. - Idempotency — endpoints that support it read an
Idempotency-Keyheader; duplicate keys within the dedup window return the original response verbatim. It is opt-in per endpoint — see the relevant guide.
#API reference
The complete, interactive API — every route, schema, and a Try-It console — is at
/openapi/.