Create and manage API keys
Mint an API key, choose its scopes, set an expiry, and rotate or revoke it — everything you need before your first API call.
An API key is the long-lived credential behind every integration. You never send it on ordinary requests: you exchange it once for a short-lived access token, and the token does the work.
Keys are managed in the app, not over the API — there is deliberately no endpoint that mints a credential.
Where
Settings → API Keys. Visible to owners and admins only, and only when your plan includes API access.
Before you start: is API access in your plan?
API access is sold separately from the rest of BunnyDoc. Without it:
- the API Keys screen refuses to create a key, and
POST /v1/auth/tokenreturns403 forbiddeneven for a key that already exists.
Check Settings → Billing → Resources for two entries:
| Resource | What it controls |
|---|---|
| API access | Whether /v1 works at all, and whether you can mint keys. |
| API signature requests | How many envelopes you may send through the API per period — one unit per envelope. |
If either is missing or at zero, talk to us about adding it before you build.
Create a key
Name it after the integration, not the person
Zapier — production, Billing sync, Staging sandbox. The name is how you'll recognise it when
you rotate or revoke months later, and it's all you'll have to go on — the key itself is never
shown again.
Pick the narrowest set of scopes
Scopes are per key. Grant only what the integration calls — see the table below.
Optionally set an expiry
Leave it blank and the key never expires. Set a future date and the key stops working then,
returning the same 401 as an unknown key. An expiry is good hygiene for a key you hand to a
third party or use for a time-boxed migration.
Copy the key immediately
The key looks like bd_live_… and is displayed exactly once, at creation. It is stored hashed
on our side, so we cannot show it to you again — if you lose it, rotate the key to mint a new
secret.
Keep the key server-side
A key acts on behalf of your whole company. Never put it in a browser, a mobile app, a repository, or any client you don't control. Load it from an environment variable or a secret manager.
Scopes
A scope lets a key do something. It can never let the key do more than the person who created it — the two are intersected on every request, so if that person loses a permission in the app, their keys lose it at the same moment.
| Scope | Unlocks |
|---|---|
templates:read | GET /v1/templates, GET /v1/templates/{id} |
contacts:read | GET /v1/contacts |
contacts:write | POST /v1/contacts |
envelopes:send | POST /v1/envelopes/from-template, GET /v1/envelopes/{id}/signing-links |
webhooks:read | GET /v1/webhooks, GET /v1/webhooks/events |
webhooks:write | POST /v1/webhooks, DELETE /v1/webhooks/{id} |
users:read | GET /v1/users |
Every key needs at least one scope. A typical send-and-track integration needs
templates:read, envelopes:send, webhooks:read and webhooks:write — and nothing else.
Two independent gates
Calling an endpoint requires both the scope on the key and the matching permission on its
owner. A key with templates:read whose owner can't read templates in the app gets
403 forbidden, not a result. Missing scope gives you 403 insufficient_scope instead, so the two
failures are easy to tell apart.
How many keys can I have?
Your plan sets the number of active keys per company. When you hit it, creating another is refused until you revoke one. Revoked keys don't count.
Use separate keys per environment and per integration — it's the only way to revoke one thing without breaking the rest.
Rotate a key
Rotation issues a new secret on the same key record, keeping its name, scopes and expiry. Use it for scheduled credential hygiene.
Rotation is not reversible:
- the new key is displayed once — copy it, then deploy it;
- the old secret can no longer be exchanged for a token, so
POST /v1/auth/tokenwith it returns401 invalid_api_key; - access tokens already minted from the old secret keep working until they expire — up to one hour.
So a rotation is a clean hand-over for your own deployment: finish the rollout within the hour and no request fails.
If a key has leaked, revoke it — don't just rotate it
Rotation closes the door on new tokens but leaves any token already issued from the old secret valid for the remainder of its hour. To cut off access immediately, revoke the key and mint a fresh one.
Revoke a key
Revoking kills the key permanently, and it is the action that takes effect immediately:
- the key can never be exchanged for a token again, and
- every outstanding token minted from it fails on its next request, with
401 key_revoked— you don't wait out the token's hour.
This is the right response to a suspected leak, a departing contractor, or an integration you're switching off.
What your integration should do about key errors
| You get | What happened | Do this |
|---|---|---|
401 invalid_api_key on /auth/token | The key is unknown, revoked, expired, or not live-mode — we don't say which | Stop retrying. Check the key in Settings → API Keys. |
403 forbidden on /auth/token | API access isn't in the plan | Stop retrying. This is a billing change, not a code change. |
401 invalid_token on any other call | The token expired | Exchange the key for a new token and retry once. |
401 key_revoked on any other call | The key behind the token was revoked | Stop retrying with the cached token. Load the replacement key. |
403 insufficient_scope | The key lacks a scope | Add the scope to the key (or mint one that has it). |
Don't mint a token per request
POST /v1/auth/token is capped at 20 requests per 15 minutes, per IP, because a healthy
integration needs roughly one token an hour. Cache the token for its full expires_in and refresh
shortly before it lapses.
Next steps
E-Signing API
Send documents for signature straight from your own systems — exchange an API key for a token, create envelopes from templates, manage contacts, and retrieve signing links over a simple REST API.
Get an access token
Exchange your API key for a short-lived bearer token — the first call every E-Signing API integration makes.