Authentication and keys
Bearer keys, the test and live split, scopes, and why a document from the wrong mode returns 404 instead of 403.
All documentation
Every request carries a bearer token. There are no OAuth flows, no refresh tokens, and no session cookies: a key is a long-lived secret you keep on your server.
Authorization: Bearer sk_live_...The base URL
The public API lives at its own host and has no /api segment. This is the single most common setup mistake.
WRONG https://api.documentesign.com/api/v1/account
RIGHT https://api.documentesign.com/v1/accountTest keys and live keys
A key is either sk_test_ or sk_live_, and the prefix decides which world it operates in. The two are fully isolated: a live key cannot see a sandbox document and a sandbox key cannot see a live one.
Asking for the wrong one returns 404, not 403. That is deliberate. Returning 403 would confirm the document exists, which would let a live key probe for sandbox data, so a wrong-mode id is indistinguishable from one that was never created.
mode on GET /account before you go looking for a deleted record. Mode mismatch is almost always the answer.Scopes
A key carries scopes, and by default it carries all seven. Narrow them when a key only ever does one job: a key that sends from templates has no business holding webhooks:write.
| Field | Type | Notes |
|---|---|---|
account:read | scope | Read the workspace, plan, mode and usage. |
documents:read | scope | List and read documents, and mint file download URLs. |
documents:write | scope | Create, send, cancel and remind; replace recipients and fields; mint embed links. Also required for POST /files - there is no separate files scope. |
templates:read | scope | List templates and read their roles and fields. |
templates:write | scope | Send a document from a template. |
webhooks:read | scope | List endpoints and read their delivery log. |
webhooks:write | scope | Create and delete endpoints, and replay a delivery. |
Calling an endpoint outside the key's scopes returns 403 insufficient_scope, and the message names the scope that was missing.
A key acts as the person who made it
This surprises people, so it is worth stating plainly. A key inherits the workspace role of the user who created it, evaluated fresh on every request. Two consequences follow:
- Reducing that member's role immediately shrinks what the key can reach.
- Removing them from the workspace kills the key -
401 membership_revoked.
For an integration that has to outlive any individual, create the key from an account that is not going anywhere.
Plan gating
The API is available on Business and Ultimate, including during a Business trial. Free and Growth workspaces get 402 on every endpoint, and so does a workspace that downgrades - immediately, without its keys needing to be revoked.
{
"error": {
"code": "api_not_enabled",
"message": "The developer API is available on the Business and Ultimate plans.",
"requiresUpgrade": true,
"requiredPlan": "business",
"feature": "api_enabled"
}
}requiredPlan so you can route the user somewhere useful.Other things that fail at the door
These are all checked before your request runs, so they cost you nothing against the rate limit beyond the round trip.
| Field | Type | Notes |
|---|---|---|
unauthorized | 401 | No Authorization header was supplied. |
invalid_api_key | 401 | Malformed, unknown, or revoked. Also returned when the creating user is no longer active. |
api_key_expired | 401 | The key had an expiry date and it has passed. |
membership_revoked | 401 | The user who created the key is no longer a member of the workspace. |
org_blocked | 401 | The workspace is blocked or deleted. |
ip_blocked | 403 | The workspace IP allowlist rejected the caller. It applies to API keys, not just browser sessions. |
Rotating a key
Keys are additive, so rotation needs no downtime: mint the new one, ship it, confirm traffic has moved using the Last used column in the dashboard, then revoke the old one. A workspace can hold up to ten keys at a time.
If a key is ever committed or exposed, revoke first and investigate after. See errors and limits for what a rejected request looks like.