@belvador/api
Typed client for the Belvador API (https://api.belvador.dev, OpenAPI 3.1).
Every operation is called by its OpenAPI operationId with parameters in the API's wire names (snake_case);
the operation table and parameter types are generated from the same registry as the OpenAPI document.
npm install @belvador/api
import { BelvadorApiError, createClient } from "@belvador/api";
const belvador = createClient({ token: process.env.BELVADOR_TOKEN! });
const { data } = await belvador.call("issues.list", { status: "unresolved", period: "24h", limit: 20 });
for await (const rows of belvador.pages("monitors.list", { limit: 100 })) console.log(rows.length);
try {
await belvador.call(
"monitors.create",
{
project_id: 1,
slug: "nightly-backup",
name: "Nightly backup",
kind: "crontab",
schedule: { type: "crontab", value: "0 2 * * *" },
timezone: "UTC",
checkin_margin_min: 5,
max_runtime_min: 30,
failure_threshold: 1,
recovery_threshold: 1,
expected_environments: ["production"],
},
{ idempotencyKey: crypto.randomUUID() },
);
} catch (e) {
if (e instanceof BelvadorApiError && e.code === "API_SCOPE_INSUFFICIENT") console.log("needs", e.scope);
}
- Tokens: a personal token (
blv_pat_), service token (blv_svc_) or OAuth access token (blv_oat_). Create one in Settings → API tokens. The client never puts the token in an error message. - Errors are RFC 9457 problem+json, surfaced as
BelvadorApiErrorwith a stablecode,status,scope(forAPI_SCOPE_INSUFFICIENT),retryAfterandrequestId. - Approvals: sensitive operations (keys, members, tokens, alert destinations, node changes) throw
APPROVAL_REQUIREDwithe.approval={ id, url, expiresAt, state }. A person opensurlon belvador.dev and approves with their authenticator code; call again with the sameidempotencyKey(orcall("approvals.get", { approval_id })) to read the outcome. Only https/approve/<id>links are kept. - Retries: reads, and writes sent with an
idempotencyKey, are retried on 429 / 502–504 and network errors (retries, default 2), honouringRetry-Afterup to 10 s.NODE_OFFLINEis not retried. - Scopes:
await belvador.scopes()returns the token's effective scopes (theBelvador-Scopesheader). - Node data (issues, events, spans, traces, logs) lives on your workspace's own node. With
nodeReads: "edge"the client asksPOST /v1/ticketsfor a short-lived read ticket and readsedge.belvador.devdirectly, so the data never passes the API gateway; it falls back to the gateway when the token may not hold a ticket.
Command line
belvador-api privacy pull writes a project's privacy policy as the JSON the Belvador SDKs accept
(privacyPolicy(), SDK 0.6.0+), so a build can ship the same rules your node applies:
BELVADOR_TOKEN=blv_svc_… npx -p @belvador/api belvador-api privacy pull --project 42 --out belvador-privacy.json
--project is the project's number (the end of its DSN). The token needs projects:read; it is read
only from BELVADOR_TOKEN and never printed. Exit codes: 0 written, 1 refused by the API (the code is
printed), 2 wrong usage.
Node ≥ 20.10, Bun, Deno and modern browsers (any runtime with fetch and AbortSignal.any). MIT licensed.