Quickstart
From zero to a signed PDF in under five minutes — terminal-only, no SDK.
This guide goes deeper than the home page summary. By the end you'll have downloaded a real PAdES-sealed PDF, seen a webhook fire against your endpoint, and you'll know which knobs to turn for production.
Every URL below uses the canonical host https://api.wesign.now/v1.
The API host from before the rename stays a permanent alias of the same
API — see Base URL for what is and isn't
the contract.
Prerequisites
- A workspace on Enterprise. API keys and the template endpoints both ship with it; Free, Pro and Branded can't mint keys. Only a workspace owner or admin can.
- Legacy Teams workspaces can still mint keys and use everything on
this page. What they can't do is send from templates:
instantiate,generateandconfirmanswer402 enterprise-requiredthere (and so does minting an embedded signing session). Listing and reading templates (GET /v1/templates,GET /v1/templates/{id}) works on Teams too. - A test PDF on disk. Any PDF works; if it doesn't have an anchor marker we just append a signature page.
- An email address you can check (or two — sender + signer can be the same address while you smoke-test).
Sending from a template instead of a PDF? The Template quickstart walks through that flow.
1 · Mint an API key
In your workspace, open
Developers → API keys and hit Create API key.
Copy the wsk_live_… plaintext to a terminal env var:
export WSK_KEY="wsk_live_…" # paste the whole key hereThe plaintext shows once. The dashboard keeps a wsk_live_a1b… prefix
so you can identify the key later. A key minted before the September
2026 rename starts with lsk_live_ and works exactly the same — no
need to re-mint.
2 · Send a real request
curl -X POST https://api.wesign.now/v1/signing-requests \
-H "Authorization: Bearer $WSK_KEY" \
-H "Idempotency-Key: smoke-$(date +%s)" \
-F "file=@/path/to/contract.pdf" \
-F 'signers=[{"email":"you@example.com","role":"signer"}]' \
-F 'placement=auto_append' | jq .Successful response (HTTP 201):
{
"documentId": "8a1e4f9a-b3c1-4d22-9e0a-3a92ee7c1f88",
"metadata": null,
"status": "pending",
"placement": "auto_append",
"presentedSha256": "4f9a…d21c",
"anchors": { "found": 0, "fields": 2 },
"signers": [
{
"role": "signer",
"email": "you@example.com",
"status": "pending",
"signingUrl": "https://yourco.wesign.now/en/sign/abc…",
"signingRequestId": "11111111-…",
"emailed": true,
"require_sms_verification": false,
"phone_masked": null,
"sms_verified_at": null,
"sms_gate": "before_sign",
"signature_level": "SES",
"reference": null,
"company": null,
"job_title": null
}
],
"observers": [],
"warnings": []
}"placement": "auto_append" with anchors.found: 0 means the PDF had no
anchor markers, so a signature page was appended.
emailed: true says we sent the invitation; the email carries the same
link as signingUrl. It is false when the send failed, or when the
address is on a domain that accepts no email (we do not send what can only
bounce). Open signingUrl in another browser tab to play the
signer side. To deliver the link yourself instead, send
-F 'send_emails=false': everything else is created as usual and
emailed comes back false.
3 · Watch a webhook fire (optional)
Skip if you'd rather poll. To see the events live, the easiest path is a free Webhook.site URL:
# webhook.site/<your-token> issued at https://webhook.site
curl -X POST https://api.wesign.now/v1/signing-requests \
-H "Authorization: Bearer $WSK_KEY" \
-F "file=@contract.pdf" \
-F 'signers=[{"email":"you@example.com","role":"signer"}]' \
-F 'callback_url=https://webhook.site/YOUR-TOKEN'The response now includes a callback.secret — store it. Each event
we POST to your URL is signed. The same three values arrive under two
header names; the X-LetsSign-* family is the pre-rename spelling and
is kept forever, so read whichever you like:
X-WeSign-Signature: t=1757404800,v1=9c3b…a2f1
X-WeSign-Event-Id: evt_…
X-WeSign-Event: signing_request.signed
X-LetsSign-Signature: t=1757404800,v1=9c3b…a2f1
X-LetsSign-Event-Id: evt_…
X-LetsSign-Event: signing_request.signedVerify the signature before trusting the body.
4 · Sign
Open signingUrl on your phone (real test) or in another browser
tab (lazy test). Sign. Watch the inbox for the completion email and
your webhook receiver for signing_request.signed →
document.completed.
5 · Pull the signed PDF programmatically
DOC_ID=8a1e4f9a-b3c1-4d22-9e0a-3a92ee7c1f88 # the documentId from step 2
curl -LOJ -H "Authorization: Bearer $WSK_KEY" \
https://api.wesign.now/v1/documents/$DOC_ID/signedReturns the sealed PDF, ready for your DMS: every signer's signature on
the page under one PAdES seal. The RFC 3161 time stamp is kept with our
records (tsa_provider and tsa_signed_at on document.completed, and on
the audit certificate), not embedded in the PDF. This is the
same URL document.completed hands you as signed_pdf_url: it needs
your key, so pin that fetch to api.wesign.now and send the bearer —
see signed_pdf_url.
In production, fetch it on document.completed, compare the event's
sha256 and store it on your side: a workspace can have signed PDFs
deleted after a set number of days. The whole flow is in
Retrieve the signed document.
Testing without a sandbox
There are no test keys yet: every key is live, every call creates real documents and sends real email and SMS, and SMS codes count against the monthly allowance. Three switches keep a test contained:
send_emails: falseonPOST /v1/signing-requestsorinstantiate— the document and the signing links are created and returned, no invitation is emailed, not even to the next signer of a sequential send when their turn comes. Open the links yourself. Automatic reminders (7 and 14 days after the send, by default) and the completion emails still go out, so use addresses you own, and withdraw what you leave unsigned.validate_only: trueoninstantiateandgenerate— the full check (oninstantiatethe SMS allowance included) and nothing is created. See Templates → dry run.review: trueoninstantiate(orgenerate) — the document is built and staged, nothing is sent. Look at it at thereview_url, then throw it away withPOST /v1/documents/{id}/discard. See Review before sending.
A sandbox with simulated email and SMS is planned — see Versioning → No sandbox yet.
What's next
- Template quickstart — send the same contract many times from a locked template.
- Placement modes — pick anchors, auto-append, or explicit coordinates.
- Second factor by SMS — require a one-time code on the signer's phone before they can sign.
- Webhooks — production-grade event delivery, retries, signature verification.
- Idempotency — safely retry network blips.
- Errors — full code table.
