API quick start
Create an API key, make your first request, and learn how errors, limits, retries and pagination work in the eSignStack API.
Create an API key
- Open Developers → API keys and click Create key.
- Enter a Key name and, if you want, an Expiry.
- Choose the Key mode: Test for sandbox data or Live for real envelopes.
- Choose abilities. Read only, Send envelopes and Full access fill in common sets; you can change them one by one.
- Copy the secret from the dialog. It's shown once and can't be retrieved later.
Workspace owners, admins and developers can manage keys. Developers can only issue keys with the webhooks:read, webhooks:write and keys:write abilities. To revoke a key, open its ⋯ menu and click Revoke, or call DELETE /api/v1/api_keys/{key}.
Keep secrets on your server. Never put a key in browser code, a mobile app, logs or source control.
Key types
| Prefix | Key | Scope |
|---|---|---|
rk_live_ |
Workspace key, live | One workspace. Created under Developers → API keys. |
rk_test_ |
Workspace key, test | The same workspace's sandbox data. |
sk_live_ |
Platform secret key | Every workspace the platform owns. Created in the platform console. |
sk_test_ |
Platform secret key, test | Sandbox data of the platform's workspaces. |
A key only sees data in its own mode: a test key can't read live envelopes, and a live key can't read test ones. Test keys create and send watermarked test envelopes, from a file or a template, and nothing counts toward your limits. SMS verification is refused in test mode (422), and changing brand kits, folders, filing rules or identity providers needs a live key (403 live_key_required). See Sandbox for more. A platform key acts inside a workspace when you send Workspace-Id: ws_…; see the platform integration guide.
Abilities
Every key lists its abilities explicitly. There are no wildcards.
| Ability | Allows |
|---|---|
envelopes:read |
Read envelopes, recipients, documents and fields |
envelopes:write |
Create and edit drafts, preview, duplicate, signing sessions, archive and restore, place legal holds |
envelopes:send |
Send, remind, extend and correct envelopes |
envelopes:void |
Void envelopes |
templates:read, templates:write |
Read or change templates |
folders:read, folders:write |
Read or change folders and filing rules |
files:write |
Upload files |
artifacts:read |
Download completed PDFs, one by one or as a ZIP |
webhooks:read, webhooks:write |
Read events; manage webhook endpoints |
brand_kits:read, brand_kits:write |
Read or change branding |
usage:read |
Read usage counters |
workspaces:export |
Export workspace data |
audit:read |
Read the workspace activity log (GET /audit_events, live keys only). Never in a preset |
keys:write |
List, create and revoke keys |
Platform keys can also hold platform:read (read the platform's settings and members), workspaces:read, workspaces:write, login_links:write and identity_providers:write. A key created through the API (POST /api/v1/api_keys) has the same mode as the key that created it, so a test key can only issue test keys. It also can't have abilities its creator doesn't have, and can't outlive it.
Make your first request
The base URL is https://esignstack.app/api/v1. Send the key as a bearer token. This request returns the key's own details and needs no ability:
curl https://esignstack.app/api/v1/api_keys/current \
-H "Authorization: Bearer $ESIGNSTACK_KEY"Objects use prefixed IDs such as env_…, rcp_…, tpl_… and file_…. JSON keys are snake_case. Every object has object, id, created (a UTC timestamp such as 2026-11-02T18:15:00+00:00) and livemode.
Create an envelope from a template
Get the template's role labels with GET /api/v1/templates/{template}, then map each person to a role:
curl -X POST https://esignstack.app/api/v1/templates/$TEMPLATE_ID/envelopes \
-H "Authorization: Bearer $ESIGNSTACK_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-456-create" \
-d '{
"name": "Acme service agreement",
"recipients": [
{"role_label": "Client", "name": "Jane Doe", "email": "jane@example.com"}
],
"external_id": "order-456",
"send": false
}'Roles that the template always sends to the same person (recipient_email on the role) are filled in for you, so you can leave them out. Include one to send that role to someone else on this envelope.
send defaults to true. With send: false the envelope stays a draft so you can check it, then send it with POST /api/v1/envelopes/{envelope}/send. values fills template fields by label. To start from your own PDF instead, see the Envelopes API.
Idempotency keys
Send an Idempotency-Key header (up to 255 characters) on POST requests, and reuse it when you retry the same request after a timeout.
- A repeat of a completed request returns the stored response with
Idempotent-Replayed: true. - The same key with a different request returns
409 idempotency_key_reused. - A request still running returns
409 idempotency_key_in_progresswithRetry-After. - Requests that fail don't use up the key. Keys are kept for 24 hours.
- The header is ignored on
POST /verify,POST /login_identities/{id}/deprovisionandPOST /identity_providers/{idp}/subjects/{sub}/deprovision.
Errors
Errors return JSON with an error object:
{
"error": {
"type": "invalid_request_error",
"code": "validation_failed",
"message": "The name field is required.",
"param": "name",
"doc_url": "https://esignstack.app/docs/api#validation_failed",
"request_id": "req_01J…"
}
}| Status | Common codes |
|---|---|
| 400 | invalid_request, workspace_required (a platform key without Workspace-Id), api_version_unsupported |
| 401 | authentication_required: missing, invalid or expired key |
| 403 | permission_denied: missing ability, suspended workspace; live_key_required |
| 404 | resource_missing: not found in this workspace and mode |
| 409 | resource_conflict, correction_requires_void, idempotency codes |
| 422 | validation_failed |
| 429 | rate_limit_exceeded |
Every response has a Request-Id header. Include it when you contact support.
API envelope limits
API and MCP envelopes have their own monthly allowance. An envelope counts when it's sent through the API or MCP in live mode, or when the API issues a signing session for an envelope that was sent another way. Test envelopes never count.
| Plan | API and MCP envelopes |
|---|---|
| Free | Part of the plan's 5 envelopes a month (app and API together) |
| Starter | 50 a month |
| Pro | 200 a month |
| Platform | 500 a month included, then 25¢ each |
Yearly plans get 12 months of allowance up front (for example, 600 on Starter). On Platform, envelopes past the allowance are billed as you go. You can set a monthly spending cap on extra envelopes on the Billing page.
When a send is refused, the API returns 422 with code validation_failed, param set to document, and a quota object that says which limit was hit and when it resets:
"quota": { "code": "api_envelope_limit_reached", "resets_at": "2026-11-01T00:00:00Z" }quota.code |
Meaning |
|---|---|
envelope_limit_reached |
The Free plan's 5 envelopes are used |
api_envelope_limit_reached |
The plan's API allowance is used |
api_spending_cap_reached |
Platform: the spending cap on extra envelopes is reached |
api_overage_unavailable |
Platform: extras are paused, for example an unpaid invoice |
GET /api/v1/usage (ability usage:read) returns current usage, including api_envelopes with used, limit, remaining and resets_at. A platform key without Workspace-Id also gets platform, the platform's own allowance, extras and spending cap.
Rate limits
Each key can make 100 requests a second, and each workspace 6,000 requests a minute across all its keys. Responses carry RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and RateLimit-Policy. Over the limit you get 429 with Retry-After; wait that long before retrying.
Versions
The current version is 2026-10-14. Each key is pinned to the version current when it was created. Send Api-Version to choose a version per request; responses echo the version used. New fields can appear in responses at any time, so ignore fields you don't recognize.
Pagination
List endpoints return object: "list", data, has_more and url, newest first. Use limit (1 to 100, default 20) and pass the last ID you received as starting_after to get the next page, or the first ID as ending_before to go back. Some endpoints take expand[], for example expand[]=recipients on envelopes.
The full list of endpoints, fields and filters is in the API reference.