LimeSignAPI Reference
LimeSign Developer API

API Reference

A REST API for electronic signatures. Create an envelope, attach PDF documents, place signature and data fields, and send it — LimeSign emails each signer a secure link, collects their input in order, and seals the finished PDF with a certificate of completion.

Base path. Every endpoint below lives under /v1 and speaks JSON (documents are the one exception — you upload raw PDF bytes). Take the base URL from the next section and prepend it to each path.
Getting started

Base URL

All API traffic goes to a single host over HTTPS. Prepend it to every path in this reference.

Production
https://api.lime-sign.com
Emails are delivered to real inboxes; envelopes, sealed PDFs, and certificates are retained. Manage keys and envelopes in the dashboard.
Keys & dashboard: https://app.lime-sign.com

Every path below is relative to this base URL — for example, POST /v1/envelopes means POST https://api.lime-sign.com/v1/envelopes.

Getting started

Authentication

The API authenticates with a secret API key sent as a bearer token. Keys are organization-scoped: one key represents your whole organization, and every request it makes reads and writes that organization's data.

Getting a key

Sign in to the dashboard and open Organization Settings → Developer (/settings). Create a key, give it a name, and copy the secret:

# A key looks like this — shown in full only once, at creation:
lsk_live_9f2c…<44 url-safe characters>
Store it immediately. LimeSign keeps only a hash and a short prefix of each key — the full secret is displayed once and can never be retrieved again. If you lose it, revoke the key and create a new one. Treat keys like passwords: never commit them or expose them in a browser.

Making an authenticated request

Send the key in the Authorization header on every /v1 request:

curl https://api.lime-sign.com/v1/envelopes \
  -H "Authorization: Bearer lsk_live_9f2c…"

A missing or malformed token returns 401 missing bearer token; an unknown or revoked key returns 401 invalid api key. The /healthz and /readyz probes are the only unauthenticated endpoints.

Getting started

Conventions

TopicDetail
FormatRequests and responses are JSON (Content-Type: application/json), except document upload, which takes a raw application/pdf body.
IdentifiersAll resource IDs are UUIDs. A malformed ID in a path returns 404, never 400.
ListsCollection endpoints wrap results in { "data": [ … ] }.
Field coordinatesField positions are page-ratios in the range [0,1] — fractions of the page width/height, so they render correctly at any zoom or page size. See Field types.
ErrorsNon-2xx responses return { "error": "message" }. See Errors.
TenancyEvery request is scoped to the key's organization automatically. You never pass an org ID.

Guide

The signing workflow

Sending a document for signature is a four-step sequence. Each step is a single API call, and the order matters — you place fields after the document exists, and you can only send once every signer has at least one field.

1 · Create an envelope

An envelope is the unit that gets signed: a title, one or more signers, and (optionally) a signing order. POST /v1/envelopes returns the envelope plus its signers, each with a generated ID.

2 · Add a document

Upload a PDF with POST /v1/envelopes/:id/documents. LimeSign records it, hashes it, and rasterizes each page to an image so signers can view it on any device. The response includes the document's id and pageCount.

3 · Place fields

Tell LimeSign where each signer signs. POST /v1/envelopes/:id/fields takes a batch of fields, each tying a signerId and documentId to a type and a position on a page.

4 · Send

POST /v1/envelopes/:id/send validates the envelope, flips it to sent, and emails the first signing group a secure link. As each signer finishes, the next group is notified automatically.

Before you can send, the envelope must be a draft, have at least one document, and have every signer covered by at least one field. Otherwise send returns 400 with a message naming what's missing.

Full example

# 1. Create an envelope with two signers
ENV=$(curl -s https://api.lime-sign.com/v1/envelopes \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Mutual NDA","signers":[
        {"name":"Ada Lovelace","email":"ada@example.com","signingOrder":1},
        {"name":"Alan Turing","email":"alan@example.com","signingOrder":2}]}')
ENV_ID=$(echo "$ENV" | jq -r .id)
SIGNER1=$(echo "$ENV" | jq -r '.signers[0].id')

# 2. Upload the PDF (raw bytes)
DOC=$(curl -s https://api.lime-sign.com/v1/envelopes/$ENV_ID/documents \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/pdf" \
  -H "X-Document-Name: nda.pdf" \
  --data-binary @nda.pdf)
DOC_ID=$(echo "$DOC" | jq -r .id)

# 3. Place a signature field for the first signer on page 1
curl -s https://api.lime-sign.com/v1/envelopes/$ENV_ID/fields \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d "{\"fields\":[{\"documentId\":\"$DOC_ID\",\"signerId\":\"$SIGNER1\",
        \"type\":\"signature\",\"page\":1,\"x\":0.6,\"y\":0.1,\"w\":0.25,\"h\":0.06}]}"

# 4. Send it
curl -s https://api.lime-sign.com/v1/envelopes/$ENV_ID/send \
  -H "Authorization: Bearer $KEY" -X POST
# → {"status":"sent","notified":1}
Guide

Field types

A field is a rectangle on a page, assigned to one signer. Its position is given as page-ratios: x/y is the top-left corner and w/h the size, each a fraction of the page. All four are in [0,1], and x+w and y+h must not exceed 1 (the field has to fit on the page).

typeWhat the signer does
signatureApplies their adopted signature (typed or drawn).
initialApplies their initials.
dateAuto-fills the date signed.
textTypes free text.
dropdownSelects from a list.
checkboxChecks or clears a box.
attachmentUploads a supporting file.

Each field also takes an optional required boolean (defaults to true).

Guide

Envelope lifecycle

statusMeaning
draftBeing assembled. Documents and fields can be added; not yet sent.
sentOut for signature. One or more signers have been notified.
completedEvery signer has signed. The sealed PDF and certificate are available.
declinedA signer declined. The envelope is stopped.
voidedThe sender cancelled it. Outstanding signing links no longer work.

Signing order. Each signer has a signingOrder integer. Signers who share a number are notified together (parallel); distinct numbers go one group at a time (sequential). When you omit signingOrder, signers are numbered in the order you supply them — i.e. sequential by default.

Guide

Accounts & branding

An account is a sending profile inside your organization — it controls the branding a signer sees: the logo and accent color on the signing screen, and the sender name, reply-to address, subject, and body of the notification emails.

Pass an accountId when creating an envelope to send under that profile. Omit it and LimeSign uses your organization's default account. Accounts are created and branded in the dashboard; the API selects among them per envelope.

{ "title": "Order form", "accountId": "a1b2c3d4-…", "signers": [ … ] }
Guide

Webhooks

Instead of polling, let LimeSign notify your server when an envelope changes. You register an HTTPS endpoint in the dashboard, and LimeSign POSTs a signed JSON event to it as things happen.

Configuring endpoints

Webhooks are managed in the dashboard under Organization Settings → Webhooks. Endpoints you add there are your organization defaults — every account uses them. You almost never need more than that. If a particular account needs to route elsewhere, add a webhook on that account to override the default for its envelopes; accounts with no webhook of their own keep inheriting the org default.

Override, not additive. When an account has its own endpoint, its envelopes deliver only to that endpoint, not also to the org default — so you never get duplicate deliveries.

Events

EventFires when
envelope.sentAn envelope is sent for signature.
signer.signedAn individual signer completes their part.
envelope.completedAll signers are done; the sealed PDF + certificate are ready.
envelope.declinedA signer declines; the envelope stops.
envelope.voidedThe sender voids the envelope.

A webhook.ping event is also sent when you click Send test in the dashboard.

Payload

The body carries the event, an envelope summary (status + signers), and authenticated API links to pull the full documents, sealed PDF, and certificate — LimeSign never puts file bytes in the webhook.

{
  "id": "3f1a…",                         // unique delivery id — dedupe on this
  "type": "envelope.completed",
  "createdAt": "2026-08-19T14:03:22.000Z",
  "envelope": {
    "id": "e7c1…", "title": "Mutual NDA", "status": "completed", "accountId": null,
    "signers": [ { "id": "9a3b…", "name": "Ada", "email": "ada@x.com",
                  "status": "signed", "signingOrder": 1, "signedAt": "2026-08-19T14:03:20Z" } ]
  },
  "signer": null,                        // set for signer.* events
  "links": {
    "self": "https://api.lime-sign.com/v1/envelopes/e7c1…",
    "documents": "https://api.lime-sign.com/v1/envelopes/e7c1…/documents",
    "certificate": "https://api.lime-sign.com/v1/envelopes/e7c1…/certificate"
  }
}

Verifying the signature

Every request carries an X-LimeSign-Signature: t=<unix>,v1=<hmac> header. The signature is HMAC-SHA256 over "{timestamp}.{rawBody}" using your endpoint's signing secret (whsec_…, shown once when you create or rotate the endpoint). Recompute it and compare in constant time; reject if the timestamp is old to prevent replays.

import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map(kv => kv.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // replay guard
  const expected = crypto.createHmac("sha256", secret)
    .update(`${t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

Other headers: X-LimeSign-Event (the event type) and X-LimeSign-Delivery (the delivery id, same as payload.id).

Delivery & retries

  • Respond 2xx within 10 seconds to acknowledge. Any other status (or a timeout) is a failure.
  • Failures are retried with exponential backoff (up to 6 attempts, ~2m → 6h) before the delivery is marked failed. The endpoint's last status and error are shown in the dashboard.
  • Delivery is at-least-once — the same event may arrive more than once, so make your handler idempotent by keying on payload.id.
  • Endpoints must be https://. Return quickly and do slow work asynchronously.
Guide

MCP server

LimeSign ships a Model Context Protocol server, so an AI assistant (Claude Desktop, Claude Code, or any MCP client) can create, prepare, and send envelopes on your behalf — using the same API key and permissions as everything else here.

Configure

Create an API key under Organization Settings → Developer, then add the server to your client. For Claude Desktop, edit claude_desktop_config.json:

{
  "mcpServers": {
    "limesign": {
      "command": "npx",
      "args": ["-y", "@limesign/mcp"],
      "env": { "LIMESIGN_API_KEY": "lsk_live_…" }
    }
  }
}

That's it — npx fetches @limesign/mcp from npm, no local checkout needed. Set LIMESIGN_API_BASE_URL too if you're pointing at a non-default host.

Tools

ToolWhat it does
list_envelopes · get_envelopeBrowse your envelopes and their status.
create_envelopeCreate a draft with a title and signers.
add_documentUpload a local PDF to a draft.
place_fields · list_fieldsPlace and review signature/date/text fields.
send_envelopeSend a draft for signature.
download_sealed · download_certificateSave the sealed PDF and Certificate of Completion.
Try: "Create an envelope titled Mutual NDA, add ~/nda.pdf, place a signature and date field for Ada on page 1, and send it to ada@example.com." The assistant chains the tools for you.

Reference

API reference

Every endpoint — its request, response, status codes, and field‑level schemas — lives in the interactive reference, generated from our OpenAPI spec (the single source of truth), so it stays in lockstep with the live API.

Open the API reference →    Download the OpenAPI spec

Import openapi.json into Postman, Insomnia, or your codegen tool to scaffold a client in minutes.


Reference

Errors

Errors return a non-2xx status and a JSON body of the shape { "error": "message" }.

StatusMeaning
400The request was malformed or failed a rule (bad field, missing document, coverage gap). The message says what.
401Missing, malformed, or invalid/revoked API key.
404Resource not found in your organization — or a malformed ID in the path.
409Conflict with current state, e.g. sending an envelope that's already sent.
500An unexpected server error. Safe to retry idempotent reads.