SDK

Dynamic env vars

Most apps use process.env.YOUR_KEY — see App env vars. That's fine for static values injected at deploy time.

Use leash.env.get when you need something process.env can't do: rotation without redeploys, an audit trail of every read, or centralized revocation across every app in the org.

Use it

app/api/my-route/route.ts
import { Leash } from '@leash/sdk/leash'
export async function GET(req: Request) {
const leash = new Leash({ request: req })
// Single key
const stripeKey = await leash.env.get('STRIPE_SECRET_KEY')
// Force-fresh — bypass the in-memory cache
const fresh = await leash.env.get('STRIPE_SECRET_KEY', { fresh: true })
// Bulk fetch
const { OPENAI_API_KEY, STRIPE_KEY } = await leash.env.getMany([
'OPENAI_API_KEY',
'STRIPE_KEY',
])
return Response.json({ ok: true })
}

The TypeScript SDK (@leash/sdk) is the reference implementation of the 0.4 Leash() client shape. Python, Go, Ruby, Java, and Rust SDKs are tracking the same shape — aspirational samples shown here; separate parity tickets track each language SDK rebuild.

Where the values come from

Org owners configure a secret source on the dashboard. Options:

  • Native — type values directly into Leash. Encrypted with the org's KMS key. Free on every plan.
  • External sources (Growth+) — point Leash at your existing Doppler / 1Password / Bitwarden / Infisical / GCP Secret Manager. Leash never stores the secret value, only a token to fetch it.

The call is the same regardless of source. Rotation in your source takes effect on the next call (results are cached for the lifetime of the Leash instance — pass { fresh: true } to bypass).

Failure modes

All SDK errors are LeashError — see Error handling for the full code list. The most common ones for env reads:

  • NO_API_KEYLEASH_API_KEY is missing.
  • UNAUTHORIZED — the API key is invalid or revoked.
  • INTEGRATION_ERROR — the external secret source returned an error (rejected token, source down). Org owners are notified via the dashboard.

When a key isn't configured in the secret source, get resolves to null and getMany omits it from the returned record — no exception.

How rotation behaves

Rotating a value in your source updates Leash immediately, but when running app code sees the new value depends on how it reads the secret:

Read mechanismRotation behavior
process.env.XBake-time. The value is mounted into the Cloud Run revision at deploy. Re-deploy (or hit “sync now”) to pick up a new value.
await leash.env.get('X')Runtime fetch. The next call after rotation gets the new value (per-instance cache, ~60s); { fresh: true } bypasses the cache.
Custom MCP bearerPer-call resolve. The token is fetched fresh via getCustomMcpConfig on every call — picks up the new value on the next call.

Plan gating

The runtime fetch (leash.env.get) is on the Growth plan or above, regardless of which source the value lives in. Dashboard-native values are still free to store and read via process.env — the gate is on the runtime fetch surface, not the source. Calls from a Starter or free org receive HTTP 402 with UPGRADE_REQUIRED.