Lystsign

Lystsign API vv1

Server-to-server REST API. Authenticate with `Authorization: Bearer ls_live_...` (or `ls_test_...`). Requests that carry an `Origin` header are refused: keys never belong in a browser. Errors are RFC 9457 problem details. Every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`. Webhooks are signed with HMAC-SHA256: `Lystsign-Signature: t=<unix>,v1=<hex>` over `<t>.<raw body>`. Test mode: `ls_test_` keys create test envelopes (`livemode: false`). They use no plan allowance, have their own rate limits and daily send cap, only email verified members of your workspace (everything else goes to the console Test outbox), are sealed with a TEST — NOT LEGALLY BINDING watermark, can never become live, and are deleted after 30 days. Live and test objects are invisible to each other (404).

Machine-readable spec: /api/v1/openapi.json

Authentication

Every call sends Authorization: Bearer <credential> from your server. Requests carrying an Origin header are refused, so browser apps must call through their own backend.

API keys (ls_live_…, or ls_test_… for test mode) are for your own workspace. Create them under Admin → Developers; a key acts as the member who created it.

OAuth 2.0 is for partner apps acting for another workspace. Register the app under Admin → Developers → OAuth apps (confidential apps get a client secret; public apps such as CLIs and native apps use PKCE alone), then run the authorization code flow:

  1. Send the user to GET /api/v1/oauth/authorize?response_type=code&client_id=…&redirect_uri=…&scope=…&state=…&code_challenge=…&code_challenge_method=S256. The redirect URI must exactly match a registered one. PKCE (S256) is required for public clients and recommended for all.
  2. The user signs in, picks a workspace where they are an owner, admin or developer, and approves. We redirect to your URI with code and your state, or with error=access_denied. Codes are single-use and expire after 2 minutes.
  3. POST /api/v1/oauth/token (form-encoded) with grant_type=authorization_code, code, redirect_uri and code_verifier. Authenticate with HTTP Basic (client_secret_basic) or client_id/client_secret in the body; public clients send only client_id.
  4. You get an access token (lso_at_…, 1 hour) and a refresh token (lso_rt_…, 30 days). Refresh with grant_type=refresh_token; every refresh returns a new refresh token, and replaying an old one revokes all tokens from that authorization. A refresh may narrow scope but never widen it.
  5. Revoke with POST /api/v1/oauth/revoke (RFC 7009, token=…). Revoking a refresh token revokes its access tokens too. Workspace admins can disconnect an app at any time under Developers → Connected apps; its tokens stop working immediately.

An OAuth token acts as the member who approved it, within the granted scopes, and counts against the workspace's plan limits and rate limits. Changes made through it are recorded in the audit log with the app's name. Errors follow RFC 6749 (invalid_grant, invalid_client, …).

Scopes

Test mode

Create a test key (ls_test_…) under Admin → Developers to build against Lystsign without sending real, binding documents. Everything a test key creates carries livemode: false, and so does every API response and webhook event about it.

OAuth access tokens always act in live mode for now.

Client-side sealed envelopes

On Enterprise, a sender can encrypt a document in the browser before upload. Lystsign stores only ciphertext, so the API cannot return it: GET /envelopes/{id}/sealed answers 409 with problem type https://docs.lystsign.com/api/problems/client-side-sealed. Envelope metadata, status, events, webhooks and the certificate of completion work as usual. Client-side sealed envelopes are created in the console only; the API cannot create them.

Endpoints

GET /api/v1/envelopes

List envelopes, newest first

Scope: envelopes:read

POST /api/v1/envelopes

Create a draft envelope (optionally with recipients)

Scope: envelopes:write

GET /api/v1/envelopes/{id}

Get an envelope with documents, recipients and fields

Scope: envelopes:read

POST /api/v1/envelopes/{id}/documents

Upload a document (raw body): PDF, Word, Excel, PowerPoint, OpenDocument, RTF, plain text, PNG or JPEG. PDFs are scanned at once; other files return status "converting" and become a PDF in the background (document.converted / document.conversion_failed webhooks). The bytes decide the type; name the file with file_name.

Scope: envelopes:write

POST /api/v1/envelopes/{id}/recipients

Add a recipient to a draft

Scope: envelopes:write

POST /api/v1/envelopes/{id}/fields

Place a field for a recipient

Scope: envelopes:write

POST /api/v1/envelopes/{id}/send

Send a draft. Counts against the monthly envelope allowance (hard cap).

Scope: envelopes:write

POST /api/v1/envelopes/{id}/void

Void a draft or sent envelope

Scope: envelopes:write

GET /api/v1/envelopes/{id}/sealed

Download the sealed PDF (409 until sealing finishes)

409 `not-ready` while sealing is still running (retry later). 409 `client-side-sealed` when the envelope is an Enterprise client-side sealed envelope: the server only holds ciphertext and cannot produce the PDF; open it in the console with an access code instead. The certificate of completion is still available.

Scope: envelopes:read

GET /api/v1/envelopes/{id}/certificate

Download the certificate of completion

Scope: envelopes:read

POST /api/v1/envelopes/{id}/ai/field-suggestions

AI field suggestions for a draft (nothing is placed). Needs AI and field detection on in Admin → Settings. Uses AI credits: 1 per started 10 pages. 402 when the monthly AI credit cap is reached; 403 when AI is off or the envelope is client-side sealed.

Scope: envelopes:write

GET /api/v1/envelopes/{id}/ai/summary

Read the stored AI summary (no credits). 404 until one is created.

Scope: envelopes:read

POST /api/v1/envelopes/{id}/ai/summary

Create (or replace) the AI summary and risk flags. Not legal advice. Uses AI credits: 2 per started 10 pages. 402 at the monthly AI credit cap; 403 when AI is off or the envelope is client-side sealed.

Scope: envelopes:write

POST /api/v1/envelopes/{id}/embedded-sessions

Mint a one-time embedded signing session for an `embedded` recipient

Scope: envelopes:write

POST /api/v1/envelopes/from-template

Create (and optionally send) an envelope from a published template

Scope: envelopes:write

GET /api/v1/templates

List templates

Scope: templates:read

GET /api/v1/webhook-endpoints

List webhook endpoints

Scope: webhooks:manage

POST /api/v1/webhook-endpoints

Create an endpoint. The signing secret is returned once.

Scope: webhooks:manage

GET /api/v1/webhook-endpoints/{id}

Get an endpoint

Scope: webhooks:manage

PATCH /api/v1/webhook-endpoints/{id}

Update URL, events or status

Scope: webhooks:manage

DELETE /api/v1/webhook-endpoints/{id}

Delete (or disable, when it has a delivery log)

Scope: webhooks:manage

POST /api/v1/webhook-endpoints/{id}/rotate-secret

Rotate the signing secret

Scope: webhooks:manage

POST /api/v1/webhook-endpoints/{id}/test

Send a signed ping now

Scope: webhooks:manage

GET /api/v1/webhook-endpoints/{id}/deliveries

Delivery log, newest first

Scope: webhooks:manage

POST /api/v1/webhook-deliveries/{id}/replay

Re-deliver a logged event (same event id)

Scope: webhooks:manage

GET /api/v1/usage

Plan, envelope usage and API limits

Scope: usage:read

GET /api/v1/plans

Public plan list and prices (USD only unless INR billing is enabled)

Webhook events

envelope.created

envelope.sent

envelope.completed

envelope.declined

envelope.voided

envelope.expired

envelope.reminded

recipient.viewed

recipient.signed

recipient.declined

document.converted

document.conversion_failed