Integrator FAQ
Short answers an integrating app, or its AI assistant, can quote — where to start, the sandbox, SDK and the MCP server, signing-link hosts, observers, AES by SMS, the SMS allowance, checking phone numbers, languages, signer names, parallel signing, field keys and dates, webhooks and key scopes, and how to reach us.
The questions integrating apps send us most, answered in a few lines each, with a link to the page that holds the full contract. If you work with an AI assistant, give it llms.txt (every page with a one-line summary) or llms-full.txt (every page as plain Markdown) instead of pasting pages into a chat.
Which release? Every answer describes the API these docs were published
with. Where the behaviour changed recently, the answer names the release and
the changelog id (look it up in
/api-changelog.json), and says
what happened before it: that matters for documents created earlier.
Getting started
Where do we start?
GET /v1/mechecks the key and returns the workspace, the key'sscopes,capabilities(branch on those, never on the tier name) and this month'ssms_allowance.- Send a PDF with
POST /v1/signing-requests. To fill a locked template instead: read its contract withGET /v1/templates/{id}, ask your user for the inputs, dry-runPOST /v1/templates/{id}/instantiatewith"validate_only": true, then send the same body without it. Send anIdempotency-Keyon both creates. - Register one receiver with
POST /v1/hooks("payload": "minimal"for identifiers only), store itssecret, verifyX-WeSign-Signatureon every delivery, and check it withPOST /v1/hooks/{id}/test. - On
document.completed, fetchsigned_pdf_urlandaudit_trail_urlwith the same key, checksha256against the PDF and store both on your side: a workspace can have signed PDFs deleted after a set number of days. See Retrieve the signed document.
Reference: Quickstart, Prepare a fill in your app, Webhooks.
Is there a sandbox or a test key?
Not yet: a sandbox with test keys is planned for 2026-11-30. Every key is
live (wsk_live_…): every call creates real documents, which count toward
your plan's monthly cap, sends real
email and SMS, and SMS codes count against the monthly allowance. To test:
"validate_only": trueoninstantiateandgenerate: the full check, nothing is created;"review": trueoninstantiateorgenerate, thenPOST /v1/documents/{id}/discard: the document is built and staged, nothing is sent;"send_emails": false: no invitation email, also not to a later sequential signer; automatic reminders and completion emails still go out, and a signer who asks for an SMS code still gets one;- addresses and mobile numbers you control.
Reference: Testing without a sandbox, Versioning → No sandbox yet.
Is there an SDK or an MCP server?
There is no official SDK: generate a client from the
OpenAPI spec (https://www.wesign.now/openapi.json) or call
the REST API directly. Give an AI assistant
llms.txt or
llms-full.txt.
There is an MCP (Model Context Protocol) server, since 2026-10-22: an AI agent
connects to https://www.wesign.now/api/mcp with your API key and sends
documents for signature, tracks them, reminds and withdraws, on the REST
API's caps and limits. It never signs: every signer signs themselves. See
MCP server.
Which host are signing links on?
wesign.now. The signingUrl / signing_url the API returns is the same
link the invitation email carries:
https://<slug>.wesign.now/<locale>/sign/<token> for a branded workspace
(Enterprise and legacy Teams workspaces are), else
https://www.wesign.now/<locale>/sign/<token>, without <locale>/ when no
language was set for the signer (see Languages). Links the API returned before
late September 2026 keep their original host and keep working: the token
decides, not the domain. Store the link you get rather than building one,
and treat it as the signer's credential. An embedded embed_url is on the
workspace's own signing host — see The embed URL.
What does the API not offer?
- A sandbox or test keys (see above).
- An SDK (AI agents have the MCP server).
- Verifying a phone number by SMS without a signing request (below).
- Qualified electronic signatures (QES):
signature_levelisSES(the email link) orAES(plus an SMS code). - Manual field placement after upload: retired,
400 placement_retired(Placement modes).
Observers
How do I copy someone, such as a secretariat or a shared mailbox, on a signing request?
Send observer_emails on
POST /v1/signing-requests:
{ "observer_emails": ["sekretariat@treuhand-muster.ch"] }- An array of address strings: no objects, no names, no language per observer. At most 20 entries, counted before duplicates are removed; each address at most 254 characters.
- A shared mailbox is fine. An observer is an address, not an account.
- Addresses are stored lower-cased and without duplicates, and read back as
observersin the 201 and onGET /v1/documents/{id}. - Send plain ASCII addresses without surrounding spaces. A display-name form
(
"Office" <office@firm.ch>), a leading or trailing space or a non-ASCII domain answers400 invalid_request; send an internationalised domain as punycode (xn--…). - In a multipart body, send the array as a JSON-encoded string.
- The request key is
observer_emails;observersis only the response key. A request keyobserversis ignored and named inwarnings[](unknown_field); from 2026-12-31 (00:00 UTC) it answers400 unknown_field. - Only this endpoint takes observers:
instantiateandconfirmdo not. The list cannot be changed after the create.
Reference: Observers.
What does an observer receive, and when?
Two emails at most:
| When | Attachments | Language | |
|---|---|---|---|
| Notice ("X is collecting signatures on Y") | At the create, when the first signer is sent out. Not sent with send_emails: false. | None, and no link to the document | The request's locale, else your workspace language, else English |
| Completed | When the last signer signs and every signer has signed. Never after a decline, a withdrawal or an expiry. | The signed PDF and the audit certificate | The same (a document created before 2026-10-19: the language of the signer whose signature completed it) |
- Observers never receive a signing link, a reminder, a note per signature, or a declined, withdrawn or expired notice.
send_emails: falsestops the notice, not the "Completed" email.- An observer address that is also the completing signer, or the document's sender (the user who created the API key), receives that person's own completion email instead of the observer copy.
- Treat
document.completed, orGET /v1/documents/{id}→status: "signed", as the proof of completion — never the observer email. Both require every signer to have signed. - Before the 2026-10-01 release (
observer-completed-every-signer), when another signer had declined or been withdrawn, observers could receive "Completed" after the last remaining signature.
Are observers on the audit trail?
- Not by address: the audit certificate shows no observer addresses.
GET /v1/signing-requests/{id}→auditEventshasobserver_notified(on the first signer's request) andobserver_completed(on the completing signer's request), each with the addresses inmeta.observers. It returns the first 50 events of that request.- There are no observer webhooks.
AES signatures by SMS
What makes a signature AES, and what backs it?
Give the signer a phone_e164 and require_sms_verification: true. Then:
- The signer opens their own link and requests a code, which we text to that number; they enter it on the page. We never text a code on our own.
- Our server refuses the signature (
403) until that signer's code is verified. Only our server-side record counts, never the browser. - Every signer object of the create response and of
GET /v1/signing-requests/{id}reads backsignature_level: "AES"and, once the code is entered,sms_verified_at. - What the code proves: control of the number you supplied. We do not check that the number belongs to the named person. The verification is stored on the signing request, so any browser that has the link can then sign.
- The signed PDF is sealed with our platform's PAdES certificate (the signer
holds no key). When the time-stamp authority answers, an RFC 3161 time
stamp is taken over the sealed PDF and kept with our records
(
tsa_provider,tsa_signed_at); it is not embedded in the PDF until the seal-certificate change, from which seals carry one inside (PAdES B-T). The caption under the signature says "Electronic signature + SMS 2FA", and the audit certificate states the same on the signer's row and records the verification. - We classify this as an advanced electronic signature (AES). Whether AES meets your legal requirement is yours to assess.
Reference: SMS verification, Signer object.
What must hold for an SMS signature to work?
phone_e164is E.164 (^\+[1-9]\d{7,14}$, e.g.+41791234567) and is sent withrequire_sms_verification: true. A missing or malformed number answers400before anything is created.- It is a mobile number in a country our SMS provider serves. Only the shape
is checked at the create; a landline, blocked or unreachable number shows
when the signer requests a code, as
signing_request.sms_failed. - The workspace has a code left this month for every SMS signer. Otherwise
the create answers
402 sms_allowance_exhaustedand nothing is created. - The request is still open when the signer asks for the code: a queued, withdrawn, declined, signed or expired request gets none.
Can two signers both confirm by SMS in one parallel request?
Yes; a request takes up to 20 signers. Each signer is a signing request of its own, with its own link, number and code:
- Give each signer its own
role. Fields are assigned by role, so two signers sharing a role would each get the other's fields. - Give each signer its own
phone_e164. Codes are issued per number: when two signers share one, the code one of them enters makes the other request a new one. - Each SMS signer needs at least one code, so a two-signer request needs two codes left.
- Read Is parallel signing safe? before you rely on two signatures that land within seconds of each other.
How many SMS codes do we get, and what do they cost?
| Plan | Codes included per month |
|---|---|
| Free | 25 |
| Branded | 200 |
| Enterprise | 100 per paid seat |
| Pro (legacy) | 50 |
| Teams (legacy) | 100 per paid seat |
- There is no price per SMS and no price per AES signature: the codes are included in your subscription. Every plan has API keys, so an API integration has its plan's codes.
- Every code sent counts, resends included; a refused send does not. The counter resets at 00:00 UTC on the 1st of each month.
GET /v1/me→sms_allowance: { monthly, used, remaining, resets_at }.- When the allowance is used up, an SMS signer cannot sign until the reset. A request is never downgraded to a signature without SMS.
- If your firm needs more, email support@wesign.now with your workspace ID
and the number of codes you expect per month. We raise the workspace's
monthly allowance, included in your subscription;
sms_allowance.monthlythen shows the plan's codes plus the raise (sms-allowance-raise, since the 2026-10-01 release).
Reference: SMS verification → the monthly allowance.
Can we verify a phone number by SMS before we create a request?
No. It is not offered and not planned. A code can only be requested by the signer, on their own signing link. Check numbers in your own interface before you create the request. After the create:
signing_request.sms_failednames the reason (invalid_number,landline,blocked, …) the moment the signer requests a code;- an
sms_sentwithout a latersms_verifiedmeans our SMS provider accepted the code, not that the phone received it; PATCH /v1/signing-requests/{id}with{ "phone_e164": "+41…" }corrects the number while the request ispending,viewedorqueuedand no code has been verified. It sends nothing; the signer then requests a new code.
Reference: When no SMS arrives.
Languages
Can each signer have their own language?
Yes. Give locale per signer to choose the language of that signer's email,
SMS code and signing page: signers[].locale on POST /v1/signing-requests,
recipients[].locale on instantiate (since 2026-10-19), one of en, de,
fr, it, nl, es. Only the two-letter code is accepted (de-CH answers
400). It is optional. Without one, we use the request's locale, then the
language this person signed in last time with your workspace, then your
workspace language, then English, and the signing page follows the signer's
browser language. A signer who has their own wesign account with a language
set always gets that language.
{ "signers": [
{ "email": "anna@example.com", "role": "client", "locale": "en" },
{ "email": "jonas@example.ch", "role": "witness", "locale": "de" },
{ "email": "marie@example.fr", "role": "partner" }
] }Anna gets English, Jonas German. Marie gets the language remembered for her in your workspace if there is one; otherwise her email is in your workspace language, else English, and her signing page in her browser's language. The whole rule: Language per signer.
Follows the signer's language: their invitation email, the signing page, reminders, the SMS code (if our SMS provider refuses the language, the number's country decides), the messages of the SMS step on the signing page and their own completion email.
Does not follow it:
| Item | Language |
|---|---|
| The caption under each signature | English, 24-hour clock |
The audit certificate from GET /v1/documents/{id}/audit-trail | English |
| The audit certificate attached to completion emails | English (certificate-always-english, since the 2026-10-01 release; before, the language of the signer whose signature completed the document) |
| The observer notice and "Completed" email | The request's locale, else your workspace language, else English (observer-language, since 2026-10-19; before, the first signer's and the completing signer's language) |
| Emails to the document's sender | The language the sender uses in the app, else the request's |
Values you send, such as names, print exactly as sent; only the labels around them are English.
Does a call without any locale still work?
Yes. locale is optional on the request and on every signer, and a call
without any language is never refused. Until 2026-10-19 such a call was
English. Now each signer gets the language remembered for them in your
workspace, else your workspace language (Settings → Signing; unset for every
existing workspace until an owner sets it), else English. To stay English
whatever an owner sets, send "locale": "en".
Which language does the API show us?
The one you asked for. Each signer in the 201 of POST /v1/signing-requests, and each recipient of instantiate, carries locale;
GET /v1/signing-requests/{id}, GET /v1/documents/{id} and
signing_request.sent show the same value. A signer's own account language is
never shown, so no response tells you whether an address has a wesign
account.
Why does a signingUrl have no language in it?
No language was set for that signer: none for them, none on the request, none
remembered. The link is then https://<slug>.wesign.now/sign/<token> and the
signing page opens in the signer's browser language, else English; the email
goes out in your workspace language, else English. Once the signer has opened
the document, reminders, the SMS code and their signed copy follow the
language the page showed them. locale in the responses does not change.
Read the language from locale, never from the link.
Reference: Language per signer, Signer object, The SMS itself.
Signer names
Which name fields does a signer take?
name(at most 200 characters), orfirst_nameandlast_name(at most 100 each). If eitherfirst_nameorlast_nameis non-blank, the stored name is "first last" andnameis ignored, so send one form, not both. Put a title such as "Dr." where it should print.- The stored name prints in the caption under the signature, as the signer's
name in the seal and on the audit certificate, and is read back as
nameonGET /v1/signing-requests/{id}andGET /v1/documents/{id}. - Signer objects are strict: an unknown key answers
400 unknown_signer_field, naming it.
Can a signer's company and function print under the signature?
Yes, since the 2026-10-01 release (signer-company-job-title): each signer
takes company (at most 200 characters) and job_title (at most 120). Both
print in the caption under the signature,
Anna Muster · Geschäftsführerin · Muster AG · anna@muster-ag.ch, and on the
audit certificate, both the one attached to the completion emails and the one
from GET /v1/documents/{id}/audit-trail, labelled "stated by the sender":
we do not verify them. Template recipients and confirm take the same two fields.
Before that release, both answered 400 unknown_signer_field.
Reference: Company and function.
Parallel signing
Is parallel signing safe?
signing_mode: "parallel", the default, invites every signer at once, and
each signs whenever they like.
document.completedfires only when every signer has signed. It never fires when a signer declined, was withdrawn or expired.- The signatures of one document are sealed one at a time
(
parallel-signing-chain, since the 2026-10-01 release). A signer who presses Sign while another signature is being sealed waits a few seconds; the signing page retries by itself. The completed PDF carries every signature, however close together they land. - Before that release, a completed PDF could lack an earlier signature
although every status read
signed, whatever thesigning_mode: when two signers signed within the same few seconds, or, rarely, when the earlier signed file could not be read at the next signature. Sequential signing (signing_mode: "sequential") avoided only the first case. If a PDF completed before that release lacks a signature, email support@wesign.now with itsdocument_id. - Deduplicate
document.completedondocument_idas well as onevent_id: delivery is at least once.
Reference: What "completed" means, Webhooks.
Templates, field keys and dates
What do we have to send to fill a template?
Read the template's contract with GET /v1/templates/{id}, ask your user for
every input with required: true, dry-run the call with
"validate_only": true, fix what problems lists, offer what
missing_optional lists, then send the same body. Step by step:
Prepare a fill in your app.
Who fills a text field, we or the signer?
owner on each input of GET /v1/templates/{id} says so:
"sender": you send the value. It prints as fixed document content that no signer can change. It isrequired: trueunless the template's author made it optional or gave it a default."signer": the signer types it while signing. It is never required; you may prefill it, and the signer may change it.
An empty text field never blocks signing. Reference: Who fills a text field.
What does a field key look like?
Lowercase parts of letters, digits and underscores, each starting with a
letter, joined by single dots; at most 64 characters: client_name,
person.date_of_birth. A dotted key is one flat key of field_values, as in
{ "person.date_of_birth": "1990-03-15" }, never a nested object.
Standard field names such as person.first_name, company.legal_name or
signing.place are keys we suggest for common data. An input that uses one
reads standard: true and carries its name in six languages. Custom keys are
just as valid.
Reference: Field keys and values, Standard field names.
Which date and time formats do you accept?
- Dates:
YYYY-MM-DD, or day first:DD.MM.YYYY,DD/MM/YYYY,DD-MM-YYYYorDD MM YYYY. Month first is never read:03/04/1990is 3 April, and03/15/1990answers422 invalid_date. Stored and echoed asYYYY-MM-DD. - Times:
14:30,14.30or2:30 PM. Stored and echoed as 24-hourHH:MM. - How a value prints is the template's choice, shown read-only as
date_formatandtime_formatonGET /v1/templates/{id}.
Reference: Dates and times.
Webhooks and API keys
Which API key scopes does an integration need?
There are exactly two scopes, full and embedded; a key is workspace-wide
and gets one of them when it is created.
full(the default) may call every/v1endpoint: hooks (/v1/hooks, read and write), signing requests (create, withdraw), templates (read, instantiate), embedded sign sessions, and the signed PDF and audit trail downloads.embeddedmay call only/v1/embedded/*; anything else answers401 invalid_key.
So one full key covers an integration that manages hooks, sends, embeds
and downloads. There are no finer scopes. GET /v1/me → key.scopes shows
what a key holds.
Reference: Key scopes.
How do we get webhook bodies without personal data?
Register the hook with "payload": "minimal" on POST /v1/hooks. Every
delivery then carries only event, event_id, created_at and
document_id; signer events add signing_request_id, role and
reference, and document.completed adds signed_pdf_url, sha256 and
audit_trail_url. No names, emails, phone numbers, metadata or
workspace_id. Minimal hooks send User-Agent: wesign-webhooks/1. Since
the 2026-10-02 release (hooks-payload-minimal).
A document-scoped callback_url hook is created with the full body, and the
API cannot make it minimal. For identifiers only, register a workspace hook with
POST /v1/hooks and "payload": "minimal", and do not pass callback_url.
Reference: Payload modes.
How do we change a hook's events or payload?
POST /v1/hooks again with the same target_url and the new events or
payload. The old hook is switched off and the response is a new hook with
a new id and a new secret (store it), naming the old one in
replaced_hook_ids. Deliveries still being retried for the old hook move to
the new one. Before the 2026-10-02 release (hooks-same-url-replaces), a
second POST created a second active hook, and both received every event.
Leaving payload out keeps a minimal hook minimal; send "full" to
switch back. events is not carried over: left out, it means every event.
Deliveries of the old hook that had already given up cannot be redelivered
after the switch, so redeliver them first.
Which events should a receiver subscribe to?
signing_request.signed (once per signer), document.completed (once per
document, after the last signer, when the final PDF can be fetched),
signing_request.declined, signing_request.withdrawn and
signing_request.expired. Expiry and decline are per signer, with
signing_request_id: there is no document-level expiry event, and a
document with a declined, withdrawn or expired signer never completes, so
these three are how you learn that a case will not get a signed PDF. In a
sequential send, signers still queued behind it get no event.
Delivery is at least once: deduplicate on event_id, and
document.completed on document_id too, because a completion repeated
after a database failure carries a new event_id.
How do we get the signed PDF back into our system?
- Keep the
document_idyour create call returned (document_idofinstantiate,documentIdofPOST /v1/signing-requests) with your own record. A minimal hook's events carry nometadata, and no endpoint lists documents. - On
document.completed, verify the signature, answer2xx, thenGET signed_pdf_urlandGET audit_trail_urlwith afullkey of the workspace that owns the document. Pin the host toapi.wesign.now. - Compare the event's
sha256with the SHA-256 of the PDF bytes. Not with thesha256ofGET /v1/documents/{id}: that one hashes the file as it was sent for signature. - Retry
409 not_stored,429,5xx(including503 temporarily_unavailable, which carriesRetry-After) and timeouts with backoff, and a401on a key that worked before a few times;404and410 deleted_by_retentionare final. - Store both files. If the workspace has Delete signed documents after
N days on (Settings → Signing → Auto-delete),
/signedanswers410from the first nightly run after N days from the last signature; the API does not expose the setting. The audit trail stays available.
A two-signer document completes once, after the second signer. An envelope sent from the dashboard completes per document.
Reference: Retrieve the signed document.
Is the audit trail inside the signed PDF?
No. signed_pdf_url serves the sealed PDF, the same bytes on every fetch
until the workspace's retention period ends (then 410), and sha256 is
computed over exactly those bytes. The audit trail is a separate PDF at
audit_trail_url (same host, same key), issued once at completion and
stored: every fetch returns the same bytes, whose SHA-256 is
audit_trail_sha256 on the event (not covered by sha256), and it stays
available after retention. Fetch both if you archive both.
Reference: Documents.
What happens while our endpoint is down?
Any 2xx is a delivery. Anything else, no answer within 10 seconds, or a
redirect (we never follow one) is retried after 1 min, 5 min, 30 min, 2 h,
6 h, 12 h and 24 h, with the same event_id and a fresh signature each time.
After that the delivery gives up; GET /v1/hooks/{id}/deliveries (or the
dashboard) shows it, and …/deliveries/{delivery_id}/redeliver sends it
again. We never switch a hook off on our own. POST /v1/hooks/{id}/test
sends a signed ping to check your endpoint.
Reference: Retries, Delivery log.
Do events arrive in order, and can one go missing?
- Order is not guaranteed. Each delivery and its retries run on their
own:
document.completedcan arrive before asigning_request.signed. Act oneventand the ids; readGET /v1/documents/{id}when order matters. - Rarely, an event is never sent: when it could not be recorded for
delivery (a database failure at that moment) there is nothing to retry or
redeliver; we are alerted. If every
signing_request.signedhas arrived and nodocument.completedfollows within a few minutes, readGET /v1/documents/{id}(status: "signed") or fetch the signed PDF.
Reference: Order and reconciliation.
Asking us
How do we ask something the docs do not answer?
Email support@wesign.now. We reply within one working day. Include:
- the method and path (
POST /v1/signing-requests) and the UTC time of the call; - the HTTP status and the
codeanderrorof the answer; - the ids you have:
document_id,signing_request_id, the webhookevent_id(headerX-WeSign-Event-Id), yourIdempotency-Key, and thex-vercel-idresponse header, which finds the call in our logs; - your workspace (
GET /v1/me→workspace.slug); - what you expected instead.
Never send an API key, a webhook secret or a complete signing link.
Reference: Support.
Prepare a fill in your app
Read a template's contract, ask your user for what it needs, dry-run, then send. Required fields, missing_optional and standard field names.
MCP server
Let an AI agent send documents for signature and track them — Claude Code, Claude Desktop, Cursor, VS Code or your own agent, with your API key. The agent sends and tracks; every signer signs themselves.
