Get an access token
Exchange your API key for a short-lived bearer token — the first call every E-Signing API integration makes.
Every E-Signing API integration starts here. You send your API key once to this endpoint and get back a short-lived access token. From then on, every other request carries only the token — your key never travels again.
Endpoint
POST https://api2.bunnydoc.com/v1/auth/token
Request
The key is sent in a header. There is no request body.
| Header | Required | Value |
|---|---|---|
X-API-Key | Yes | Your API key, e.g. bd_live_a1b2c3d4e5f6…. Create one under Settings → API Keys. |
Example request
curl -X POST https://api2.bunnydoc.com/v1/auth/token \
-H "X-API-Key: bd_live_a1b2c3d4e5f6g7h8"POST /v1/auth/token HTTP/1.1
Host: api2.bunnydoc.com
X-API-Key: bd_live_a1b2c3d4e5f6g7h8const res = await fetch("https://api2.bunnydoc.com/v1/auth/token", {
method: "POST",
headers: { "X-API-Key": process.env.BUNNYDOC_API_KEY },
});
const { access_token, expires_in } = await res.json();
// Cache access_token and reuse it for the next `expires_in` seconds.import os, requests
res = requests.post(
"https://api2.bunnydoc.com/v1/auth/token",
headers={"X-API-Key": os.environ["BUNNYDOC_API_KEY"]},
)
token = res.json()["access_token"]
# Cache the token and reuse it until it expires.Response
A 200 OK returns the token and the scopes it carries.
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXlfaWQiOiI…",
"token_type": "Bearer",
"expires_in": 3600,
"scopes": ["templates:read", "contacts:read", "contacts:write", "envelopes:send"]
}| Field | Type | Description |
|---|---|---|
access_token | string | The bearer token. Send it as Authorization: Bearer <access_token> on every other request. |
token_type | string | Always Bearer. |
expires_in | integer | Seconds until the token expires — always 3600 (one hour). |
scopes | string[] | The permissions this token carries, inherited from the key. |
Using the token
Send the token in the Authorization header on every other call. For example, to list your
templates:
curl https://api2.bunnydoc.com/v1/templates \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"Errors
Every error has the same shape — branch on error.code, never on the message text.
{
"error": {
"code": "invalid_api_key",
"message": "The API key is invalid, revoked, expired, or not a live-mode key."
}
}| Status | error.code | When it happens |
|---|---|---|
401 | missing_api_key | No X-API-Key header was sent. |
401 | invalid_api_key | The key is unknown, revoked, expired, or not a live-mode key. All four return the same body — we don't reveal which. |
403 | forbidden | API access isn't included in your company's current plan. |
429 | rate_limited | More than 20 token requests in 15 minutes from one IP. Request one token per hour, not one per call. |
See the full error reference for every code the API can return.
Good to know
Tokens last one hour — cache and reuse them
expires_in is always 3600 seconds. Request a token, hold onto it, and only exchange your key
again when the token is close to expiring. The token-exchange endpoint is rate limited to 20
requests per 15 minutes per IP precisely because a healthy integration needs about one token per
hour, not one per request.
Revoking or rotating a key invalidates its tokens immediately
You don't have to wait for a token to expire. The moment a key is rotated or revoked in Settings → API Keys, every token minted from it stops working on its next request.
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.
Templates
List your E-Signing templates and read a single template's roles — the starting point for sending an envelope from the API.