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?

  1. GET /v1/me checks the key and returns the workspace, the key's scopes, capabilities (branch on those, never on the tier name) and this month's sms_allowance.
  2. Send a PDF with POST /v1/signing-requests. To fill a locked template instead: read its contract with GET /v1/templates/{id}, ask your user for the inputs, dry-run POST /v1/templates/{id}/instantiate with "validate_only": true, then send the same body without it. Send an Idempotency-Key on both creates.
  3. Register one receiver with POST /v1/hooks ("payload": "minimal" for identifiers only), store its secret, verify X-WeSign-Signature on every delivery, and check it with POST /v1/hooks/{id}/test.
  4. On document.completed, fetch signed_pdf_url and audit_trail_url with the same key, check sha256 against 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": true on instantiate and generate: the full check, nothing is created;
  • "review": true on instantiate or generate, then POST /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.

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_level is SES (the email link) or AES (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 observers in the 201 and on GET /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 answers 400 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; observers is only the response key. A request key observers is ignored and named in warnings[] (unknown_field); from 2026-12-31 (00:00 UTC) it answers 400 unknown_field.
  • Only this endpoint takes observers: instantiate and confirm do not. The list cannot be changed after the create.

Reference: Observers.

What does an observer receive, and when?

Two emails at most:

WhenAttachmentsLanguage
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 documentThe request's locale, else your workspace language, else English
CompletedWhen the last signer signs and every signer has signed. Never after a decline, a withdrawal or an expiry.The signed PDF and the audit certificateThe 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: false stops 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, or GET /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} → auditEvents has observer_notified (on the first signer's request) and observer_completed (on the completing signer's request), each with the addresses in meta.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 back signature_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?

  1. phone_e164 is E.164 (^\+[1-9]\d{7,14}$, e.g. +41791234567) and is sent with require_sms_verification: true. A missing or malformed number answers 400 before anything is created.
  2. 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.
  3. The workspace has a code left this month for every SMS signer. Otherwise the create answers 402 sms_allowance_exhausted and nothing is created.
  4. 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?

PlanCodes included per month
Free25
Branded200
Enterprise100 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.monthly then 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_failed names the reason (invalid_number, landline, blocked, …) the moment the signer requests a code;
  • an sms_sent without a later sms_verified means 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 is pending, viewed or queued and 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:

ItemLanguage
The caption under each signatureEnglish, 24-hour clock
The audit certificate from GET /v1/documents/{id}/audit-trailEnglish
The audit certificate attached to completion emailsEnglish (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" emailThe 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 senderThe 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), or first_name and last_name (at most 100 each). If either first_name or last_name is non-blank, the stored name is "first last" and name is 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 name on GET /v1/signing-requests/{id} and GET /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.completed fires 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 the signing_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 its document_id.
  • Deduplicate document.completed on document_id as well as on event_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 is required: true unless 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-YYYY or DD MM YYYY. Month first is never read: 03/04/1990 is 3 April, and 03/15/1990 answers 422 invalid_date. Stored and echoed as YYYY-MM-DD.
  • Times: 14:30, 14.30 or 2:30 PM. Stored and echoed as 24-hour HH:MM.
  • How a value prints is the template's choice, shown read-only as date_format and time_format on GET /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 /v1 endpoint: hooks (/v1/hooks, read and write), signing requests (create, withdraw), templates (read, instantiate), embedded sign sessions, and the signed PDF and audit trail downloads.
  • embedded may call only /v1/embedded/*; anything else answers 401 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?

  1. Keep the document_id your create call returned (document_id of instantiate, documentId of POST /v1/signing-requests) with your own record. A minimal hook's events carry no metadata, and no endpoint lists documents.
  2. On document.completed, verify the signature, answer 2xx, then GET signed_pdf_url and GET audit_trail_url with a full key of the workspace that owns the document. Pin the host to api.wesign.now.
  3. Compare the event's sha256 with the SHA-256 of the PDF bytes. Not with the sha256 of GET /v1/documents/{id}: that one hashes the file as it was sent for signature.
  4. Retry 409 not_stored, 429, 5xx (including 503 temporarily_unavailable, which carries Retry-After) and timeouts with backoff, and a 401 on a key that worked before a few times; 404 and 410 deleted_by_retention are final.
  5. Store both files. If the workspace has Delete signed documents after N days on (Settings → Signing → Auto-delete), /signed answers 410 from 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.completed can arrive before a signing_request.signed. Act on event and the ids; read GET /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.signed has arrived and no document.completed follows within a few minutes, read GET /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 code and error of the answer;
  • the ids you have: document_id, signing_request_id, the webhook event_id (header X-WeSign-Event-Id), your Idempotency-Key, and the x-vercel-id response 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.

On this page

Getting startedWhere do we start?Is there a sandbox or a test key?Is there an SDK or an MCP server?Which host are signing links on?What does the API not offer?ObserversHow do I copy someone, such as a secretariat or a shared mailbox, on a signing request?What does an observer receive, and when?Are observers on the audit trail?AES signatures by SMSWhat makes a signature AES, and what backs it?What must hold for an SMS signature to work?Can two signers both confirm by SMS in one parallel request?How many SMS codes do we get, and what do they cost?Can we verify a phone number by SMS before we create a request?LanguagesCan each signer have their own language?Does a call without any locale still work?Which language does the API show us?Why does a signingUrl have no language in it?Signer namesWhich name fields does a signer take?Can a signer's company and function print under the signature?Parallel signingIs parallel signing safe?Templates, field keys and datesWhat do we have to send to fill a template?Who fills a text field, we or the signer?What does a field key look like?Which date and time formats do you accept?Webhooks and API keysWhich API key scopes does an integration need?How do we get webhook bodies without personal data?How do we change a hook's events or payload?Which events should a receiver subscribe to?How do we get the signed PDF back into our system?Is the audit trail inside the signed PDF?What happens while our endpoint is down?Do events arrive in order, and can one go missing?Asking usHow do we ask something the docs do not answer?