Guides
Go from test to live
Build against a test key that spends nothing, then switch to a live key, what changes, what does not, and the checklist before you flip.
A key’s mode is baked into its prefix, dhanu_sk_test_… or dhanu_sk_live_…,
and chosen when it is created. A test key goes through authentication, scope,
validation, rate limits and idempotency exactly like a live one, then answers with
fixtures instead of running a model or starting a task, and never spends a
credit. Everything that works against a test key works against a live one.
What a test key returns
| Call | Test mode |
|---|---|
images.generate | A 1×1 PNG, same bytes every time |
text.generate / text.stream | A short reply that names itself as a fixture |
tasks.trigger / tasks.create | A task_test_… task that is created nowhere |
tasks.messages | A two-message transcript for that task |
embeddings.create | Deterministic 8-dimensional vectors |
transcriptions.create | A fixture transcript |
ocr.extract | An already-succeeded job |
videos.generate | A ready video with no bytes to download |
workflows.run | A fixture task |
Reads that need real rows, listing staff, workflows, files, tasks, return the workspace’s real data with either key, since reading spends nothing.
The checklist
- Scope the live key as narrowly as the test key. Same packages or
operations; the console shows what each key may call and
me()confirms it from code. - Set a monthly spend cap and an expiry on the live key. A cap turns a runaway
loop into a
key_spend_limit_reachederror instead of a bill. - Send idempotency keys on every
POSTthat creates something. The SDKs add one automatically when they retry; pass your own (an order id, a ticket id) so a retry from your side is safe too. - Handle
insufficient_creditsandrate_limited. A test key never returns them; a live key will, on the day the workspace runs dry or a burst hits the limit. Back off onRetry-After. - Verify webhooks with the live endpoint’s secret: each endpoint has its own.
- Keep the test key for CI. Contract tests against fixtures catch a broken integration before it touches credits.
Flipping
Mint the live key, put it in the production environment as DHANU_API_KEY, deploy.
Nothing in your code changes: the SDK reads the mode from the key, and me().key.mode
tells you which one a process is holding.
const me = await dhanu.me();
if (me.key.mode !== "live") throw new Error("production is holding a test key");
Leaked keys are revoked for you: GitHub’s secret scanning reports a dhanu_sk_
that lands in a public repository, and the key is disabled and the workspace
notified within minutes. Revoke a key yourself in the console or with
dhanu keys revoke <id>.