eSignStack for Platforms
Give each of your customers their own eSignStack workspace, created, branded and run by your server through the API.
This guide is for software platforms that add e-signatures for their customers. It covers the flows in the order you build them. Request and response schemas are in the API reference.
How it fits together
| Concept | What it is |
|---|---|
| Platform | Your organization's account. It owns customer workspaces, platform keys, platform webhooks, brand defaults and the console. |
| Customer workspace | One customer's isolated space: templates, envelopes, branding, members. ID ws_…, plus your own external_id. |
| Platform key | sk_live_… or sk_test_…. Acts on any of your workspaces when you send Workspace-Id: ws_…; without it, on platform endpoints. |
| Workspace key | rk_live_… or rk_test_…. Bound to one workspace, with an explicit list of abilities. Use one per customer to limit exposure. |
| Platform member | A person on your team who uses the platform console: an admin, developer, billing or support member. |
Keys belong on your server. Never put one in a browser or mobile app; the browser only ever opens a short-lived session URL.
Get set up
- Subscribe to the Platform plan with the account that will pay for your platform.
- Contact eSignStack. We create your platform, link it to that paying account and make you its first admin.
- Open Platform console from the workspace switcher or your user menu, invite your team, and create your first platform key.
Billing
The Platform plan is $99 a month, or $990 a year.
- In-app signing in your customer workspaces is unlimited under fair use (normal use by your customers' own organizations).
- API and MCP envelopes from all of your workspaces count toward the paying account: 500 a month are included (6,000 up front on yearly billing). Each extra envelope is 25¢, billed on the next invoice.
- An envelope counts once: when it's sent through the API or MCP, or when the API first creates a signing session for an envelope sent in the app. Test mode never counts.
- Spending cap (optional): on the paying account's Billing page, turn on Limit extras each month under Limit on extra API envelopes and enter an amount. The account gets an email at 80% and when the limit is reached. After that, API sends past the allowance fail with
api_spending_cap_reacheduntil the month resets. - Extras pause while the latest invoice is unpaid (
api_overage_unavailable).
Customer workspaces get the Platform plan's features, including custom branding, kiosk mode and signer verification. The console's Settings page shows who is billed and this period's API usage.
1. Create a workspace for each customer
Create a workspace when a customer signs up. Use your own customer ID as external_id: it's unique within your platform, so sending the same request again returns the same workspace (201 the first time, 200 after). The same external_id with different attributes returns 409.
curl -X POST "https://esignstack.app/api/v1/workspaces" \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "Idempotency-Key: customer-1001-create" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Dental",
"external_id": "customer_1001",
"metadata": { "region": "west" },
"settings": {
"embed_allowed_origins": ["https://acme.example.com"],
"signing_return_allowed_origins": ["https://acme.example.com"],
"default_signing_window_days": 14
},
"notification_policy": { "signing_request": "host", "reminder": "host" }
}'- Don't send
Workspace-Idto the/workspacesendpoints. Store the returnedid(ws_…) next to your customer. - A workspace takes the key's mode: a test key creates and lists test workspaces, a live key live ones. Test keys can create and send envelopes; test documents are watermarked and never billed.
settingsacceptsembed_allowed_originsandsigning_return_allowed_origins(origins only: no path, query or credentials; up to 50 return origins),default_signing_window_days(1–365) and the signing disclosure text (esign_disclosure_title,esign_disclosure_body,esign_disclosure_agreement_label).notification_policysays who sends each email:esignstack(we send it) orhost(we stay silent; you notify people yourself). Kinds:signing_request,reminder,sequential_handoff,completed_copy_recipients,completed_copy_cc,owner_completed,delivery_problem,declined,voided.metadatatakes up to 20 string key/value pairs. Don't store secrets there.brand_kittakes an inline kit or abk_…ID (see Branding below).
Other workspace calls:
| Task | Request |
|---|---|
| Find a customer's workspace | GET /workspaces?filter[external_id]=customer_1001 (also filter[status], filter[name]) |
| Update | PATCH /workspaces/{ws} (external_id can't change) |
| Suspend or resume | POST /workspaces/{ws}/suspend, POST /workspaces/{ws}/resume |
| Usage for reconciliation | GET /usage?workspace=ws_…&period=2026-11 |
| Your Platform allowance | GET /usage without Workspace-Id: platform.api_envelopes (allowance, extras, cap) |
| Platform settings (read-only) | GET /platform with platform:read |
| Platform members (read-only) | GET /platform/members (filter[role]); invitations and roles stay in the console |
| Export a workspace | GET /workspaces/{ws}/export with Workspace-Id and the workspaces:export ability |
A suspended workspace keeps its reads and its signing links already sent, but every write through the API or the app is blocked.
2. Keys
The simplest integration holds one platform key and names the workspace on each call:
curl "https://esignstack.app/api/v1/brand_kits/effective" \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "Workspace-Id: ws_01J8Z3Q4M7X2V5N9K6T1R0WABC"For isolation, create a workspace key per customer with only the abilities its integration needs. Call with a platform key and no Workspace-Id. The secret is returned once.
curl -X POST "https://esignstack.app/api/v1/api_keys" \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "Idempotency-Key: customer-1001-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Dental server",
"workspace": "ws_01J8Z3Q4M7X2V5N9K6T1R0WABC",
"abilities": ["envelopes:read", "envelopes:write", "envelopes:send", "templates:read", "files:write", "artifacts:read"],
"expires_at": "2027-10-01T00:00:00Z"
}'- Abilities:
envelopes:read|write|send,templates:read|write,folders:read|write,files:write,webhooks:read|write,brand_kits:read|write,artifacts:read,usage:read,keys:write,workspaces:export. Platform keys only:workspaces:read|write,login_links:write,identity_providers:write. - A key can't grant abilities it doesn't have, and can't outlive its own expiry. The new key has the same mode as the key that created it, so a test key can only issue test keys.
- List keys with
GET /api_keys, check the current key withGET /api_keys/current, revoke withDELETE /api_keys/{key}. - In the console, only platform admins can create keys.
Create and action POSTs take an Idempotency-Key (up to 255 characters); the deprovision endpoints don't. Reuse it only to retry the same request; a different body with the same key returns 409 idempotency_key_reused. Versions, errors and rate limits are in the API quick start.
3. Branding
A brand kit holds logos, colors, radius, font and email wording. Set a platform default once and override it per customer; a workspace inherits every field it leaves empty.
# Platform default: platform key, no Workspace-Id
curl -X POST "https://esignstack.app/api/v1/brand_kits" \
-H "Authorization: Bearer $PLATFORM_KEY" \
-F "name=Acme Platform" -F "primary_color=#1f4fd8" -F "radius_px=8" \
-F "font=instrument-sans" -F "support_contact=help@example.com" \
-F "logo_light=@logo-light.png"
# Customer override: same endpoint with Workspace-Id
curl -X POST "https://esignstack.app/api/v1/brand_kits" \
-H "Authorization: Bearer $PLATFORM_KEY" -H "Workspace-Id: $WS" \
-F "name=Acme Dental" -F "primary_color=#0a7d5a" -F "logo_light=@acme-dental.png"
# What signers will see
curl "https://esignstack.app/api/v1/brand_kits/effective" \
-H "Authorization: Bearer $PLATFORM_KEY" -H "Workspace-Id: $WS"- Colors are hex, the font is
instrument-sans,systemorserif, and the radius is bounded. Logos are PNG, JPEG or WebP, up to 2 MB. - Creating, updating or deleting a kit needs a live key; a test key gets
403 live_key_required. - Each owner has one kit; update it with
PATCH /brand_kits/{kit}. Sendnullfor a field to inherit it again. show_powered_by: falsehides the eSignStack credit in your workspaces.- Admins can also edit the platform default under Settings → Brand defaults in the console.
4. Webhooks and catch-up
One platform endpoint receives events from all of your workspaces. Create it with a platform key and no Workspace-Id, or in the console under Webhooks:
curl -X POST "https://esignstack.app/api/v1/webhook_endpoints" \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "Idempotency-Key: platform-endpoint" \
-H "Content-Type: application/json" \
-d '{ "url": "https://hooks.example.com/esignstack", "enabled_events": ["*"] }'Each event carries platform (plat_…) and workspace (ws_…), so you can route it to the right customer. Verify the Webhook-Signature header, answer 2xx within 15 seconds, and de-duplicate on Webhook-Id: deliveries can repeat and arrive out of order. Signature details are in Webhooks and events.
Catch up after downtime. Events stay available for 30 days. On startup and on a schedule, list what you missed and resend anything you didn't process:
curl -G "https://esignstack.app/api/v1/events" \
-H "Authorization: Bearer $PLATFORM_KEY" \
--data-urlencode "filter[created][gte]=2026-12-01T00:00:00Z" --data-urlencode "filter[type]=envelope.completed"
curl -X POST "https://esignstack.app/api/v1/events/evt_01J…/resend" \
-H "Authorization: Bearer $PLATFORM_KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" -d '{ "webhook_endpoint": "we_01J…" }'Rotate the secret with POST /webhook_endpoints/{id}/rotate_secret; expire_old_in (up to 86,400 seconds) keeps the old secret valid meanwhile. If every delivery to an endpoint fails for 5 days, it's turned off. Check its status on the console's Webhooks page and turn it back on with Enable.
5. Send envelopes
Use the platform key with Workspace-Id, or the customer's workspace key.
| Step | Endpoint |
|---|---|
| Upload a PDF and poll until it's ready | POST /files, GET /files/{file} |
| Create a draft from a ready file | POST /envelopes |
| Or create from a template | POST /templates/{tpl}/envelopes |
| Add documents, recipients and fields | /envelopes/{env}/documents, /recipients, /fields |
| Preview, then send | POST /envelopes/{env}/preview, POST /envelopes/{env}/send |
| Correct, remind, extend or void | /corrections, /remind, /extend, /void |
| Get the signed files | GET /envelopes/{env}/artifacts |
curl -X POST "https://esignstack.app/api/v1/envelopes" \
-H "Authorization: Bearer $PLATFORM_KEY" -H "Workspace-Id: $WS" \
-H "Idempotency-Key: case-77-create" -H "Content-Type: application/json" \
-d '{ "file": "file_01J8Z3Q4M7X2V5N9K6T1R0WABC", "name": "Case 77", "external_id": "case-77" }'On envelope.completed, call GET /envelopes/{env}/artifacts and download the files promptly; the URLs are short-lived. The full walkthrough is in the Envelopes API.
6. Signing and editor sessions
A signing session gives one recipient a URL to sign, hosted on eSignStack or embedded in your app. Create it on your server when the signer is ready:
curl -X POST "https://esignstack.app/api/v1/envelopes/$ENV/recipients/$RCP/signing_sessions" \
-H "Authorization: Bearer $PLATFORM_KEY" -H "Workspace-Id: $WS" \
-H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \
-d '{
"mode": "embedded",
"parent_origin": "https://acme.example.com",
"actor": { "name": "Jane Doe", "email": "jane@example.com", "external_id": "staff_17" }
}'parent_originmust be one of the workspace'sembed_allowed_origins. A hostedreturn_urlmust be on the workspace'ssigning_return_allowed_origins, which you set when provisioning the workspace or a workspace admin sets under Settings → Embedding.- The URL must be opened within 10 minutes. An open session lasts 60 minutes idle and 8 hours at most.
actoris who your app says is acting; the audit trail records it as stated by your platform.- The signing page uses the workspace's brand kit;
appearancecan adjustprimary,radius,fontanddark_mode.
An editor session lets your user prepare a draft or template in eSignStack's editor without an eSignStack login. See Embedded signing and Editor sessions.
7. Sign in your people
Your staff can open eSignStack without a separate password, two ways:
- Login links: your server requests a single-use link for a person and redirects them to it.
- Sign in with your platform: register your OpenID Connect provider and people sign in with their platform account.
Both identify a person by your own ID, never by email, and give them memberships in your workspaces that follow your decisions. See Platform sign-in.
Build order
- Create a test workspace with an
external_id; retry it to see the same workspace come back. - Create a brand kit and check
GET /brand_kits/effective. - Create a platform webhook endpoint, verify signatures, and add the
GET /eventscatch-up job. - Wire the envelope flow with idempotency keys, then download artifacts on
envelope.completed. - Add signing sessions, hosted first, then embedded.
- Add login links, then OIDC if your people also go straight to eSignStack.
- Create a workspace key per customer and keep the platform key for platform-level work.