Everything here matches the real SDK and the real API — the quickstart, the options table, and every curl example are runnable as written.
Install the SDK, then boot a client scoped to one org and one tenant:
npm install @tenavault/sdk
import { TenaVault } from '@tenavault/sdk';
const vault = await TenaVault.init({
baseUrl: 'https://api.tenavault.com',
org: 'acme',
tenant: 'meridian-capital',
sdkKey: process.env.TV_SDK_KEY!,
stream: true, // push updates over SSE — no polling
});
const limit = vault.get('limits.api_rate_per_min', 500); // fallback if unset
// stream: true above already keeps this in sync — no polling loop to writeGet an sdkKey from your org's Settings → SDK keys in the dashboard — it's a delivery-only credential, scoped to reads, separate from a user's session.
Scopes & resolution order. A key or config document can be set at up to four scopes: global, environment, template, tenant. Resolving a value for a tenant walks those in order and returns the most specific layer that's actually set — tenant beats template beats environment beats global. This is the one rule every read in TenaVault follows, no exceptions.
Snapshots. Every SDK read is served from an in-memory snapshot fetched once at init() — there's no network call on the read path. A snapshot is a content-addressed, hashed bundle: { org, tenant, environment, hash, generatedAt, config }.
Sealing. A change to governed config only takes effect once it's proposed as a change request, collects the approvals your org's policy requires, and is merged — see Changes & seals.
TenaVault.init(options): Promise<TenaVault> boots the client and throws if the initial snapshot fetch fails. Options:
vault.get<T>(key, fallback?) — a local, in-memory read. Missing keys return the fallback (or undefined).
vault.all() — the full resolved config as a read-only object.
vault.hash — the current snapshot's content hash; changes iff the config does.
vault.refresh(): Promise<boolean> — revalidate against the delivery plane (via ETag / If-None-Match). Returns whether new config was applied. A network failure or 5xx keeps serving the last known good snapshot — TenaVault being unreachable must never break your app.
vault.close() — stop background polling and streaming.
Every SDK, and anything you build yourself in another language, ultimately talks to three endpoints. Every request needs Authorization: Bearer <sdkKey>.
GET /api/delivery/{org}/{tenant} — the pointer. Optionally add ?environment=. Returns 200 with the current snapshot and an ETag header, or 304 if your If-None-Match matches the current hash.
curl https://api.tenavault.com/api/delivery/acme/meridian-capital \
-H "Authorization: Bearer $TV_SDK_KEY"
# → 200
# {
# "org": "acme", "tenant": "meridian-capital",
# "hash": "a3f9c21", "generatedAt": "2026-07-14T09:12:03Z",
# "config": { "limits.api_rate_per_min": 2000, ... }
# }GET /api/delivery/{org}/{tenant}/{hash} — an immutable, content-addressed snapshot, served with Cache-Control: public, max-age=31536000, immutable. Fetch this instead of the pointer once you already know the hash you want (from a webhook, or from the events stream below) — it skips revalidation entirely and is safe to cache on a CDN forever.
GET /api/delivery/{org}/{tenant}/events — a Server-Sent Events stream. Each frame is pushed the instant this tenant's effective config changes:
data: {"hash":"b7e12f4"}
A differing hash means "go refetch the pointer." Reconnect on a dropped stream — the SDK does this for you automatically when you pass stream: true.
Only Node/TypeScript ships as a maintained SDK today. Everything above works from any language that can make an HTTP request and hold a connection open.