waafir-esign reference
The waafir-esign server exposes Waafir's first-party e-signature service
to a connected AI client: create an envelope over a file in your organisation,
have its signatories sign (or decline), track status, and download the signed
PDF. Signatures are eIDAS AES (PAdES), produced by Waafir's own signing
engine; every signature captures signer consent (IP address, user agent,
timestamp) on a tamper-evident audit trail, and the signed PDF is stored as
the newest version of the source file.
Connection: https://app.waafir.io/api/mcp/waafir-esign — see
Setup. Read tools require a read-scope
PAT; creating, sending,
signing, declining and voiding require write scope. A token restricted to specific data rooms
can only work with envelopes over files in those data rooms — the same
boundary the data room tools enforce.
A monthly envelope quota
E-signing is metered by the envelope — one document / signing job counts once, no matter how many signatories it has. This MCP server and the in-app signing surface both draw from one shared monthly pool:
- Access requires the E-Signatures add-on or an active Waafir
subscription. Without either, every request is refused at the door with
HTTP 402, before any tool runs — a
Layer 1
rejection.
ADDON_NOT_PURCHASEDappears inside that response'smessagetext; it is not a separate machine-readable field, so branch on the 402 status, not on a code. - Your plan includes a monthly envelope bundle: Starter 10, Premium 50, Enterprise 50 per active seat (calendar month).
- Each purchased unit of the E-Signatures add-on adds 100 envelopes per month on top of your plan bundle; units stack on any plan.
- The quota is a hard ceiling: when you send an envelope at the
limit, the send is refused with the wire code
QUOTA_EXCEEDED, prompting you to buy more units or upgrade — nothing is ever charged per-envelope automatically. The message text also names an internalESIGN_QUOTA_EXCEEDEDtoken; that is prose, so branch onerror.code, which isQUOTA_EXCEEDED. Asend: truecreate that trips the ceiling still leaves the draft behind — send it once you have headroom rather than creating it again.
Quota is consumed when an envelope is sent (opened for signing), not per
signature — so signing and declining never consume quota. Reads and downloads
don't either. Check headroom any time with esign_quota_status.
Tools
Argument keys on this server are
camelCase. Unlike the other Waafir MCP servers,waafir-esigntakesfileId,envelopeId,transactionId,signingMode,fieldLayoutandconsentAffirmed— not theirsnake_casespellings. The one exception isconfirm_send, which really is snake_case. Result keys are camelCase too, exceptesign_download_signedand thepaginationblock, which are snake_case. This inconsistency is known and tracked; until it is resolved, use exactly the spellings on this page.
A misspelled key fails in one of two ways, and one of them is silent. An unrecognised argument is dropped, never rejected, so what happens next depends only on whether the key you meant was required:
- Required key → the call is refused. The argument is simply missing, and
you get
INVALID_ARGUMENT. This is the loud, safe case, and it coversesign_create_envelope(fileId),esign_get_envelope,esign_sign(envelopeIdandconsentAffirmed),esign_decline,esign_void_envelopeandesign_download_signed. - Optional key → the call succeeds and does something else. You get a
200and a well-formed result that answers a different question:esign_list_envelopes' filters are optional, sotransaction_id/file_idare discarded and you receive the unfiltered list of every envelope the token can see — a superset returned as though it were the filtered answer.- On
esign_create_envelope, a discardedsigning_mode: "sequential"leaves the envelope on its defaultparallelmode. Sending it then emails a legally-binding signature request to every signatory at once, which is precisely what asking forsequentialwas meant to prevent.transaction_idandfield_layoutare dropped the same way.
Because a dropped optional key cannot announce itself, check that the
result echoes what you asked for — the created envelope reports its own
signingMode and transactionId, and esign_list_envelopes is only
filtered if you spelled the filter correctly.
The one place this failure mode works in your favour: confirmSend
(camelCase) is not an accepted spelling. It is dropped, so send: true
then arrives unacknowledged and is refused — the send gate fails safe rather
than firing off emails.
esign_create_envelope (write)
Create an envelope over a file in your organisation, naming its signatories. Any email address is accepted — a signatory does not need a Waafir account. How each one is reached is decided when the envelope is sent, by whether that address already has a Waafir account: an account holder signs in-session (an in-app notification plus an email linking to the signing screen), and everyone else is emailed a guest signing link. The test is account existence, not membership of your organisation, so a signatory who belongs to a different organisation also signs in-session. Every pending signatory gets exactly one of the two — never both, never neither.
| Field | Type | Required | Description |
|---|---|---|---|
fileId | string (UUID) | yes | The file to be signed. Must belong to your organisation. |
signatories | array | yes | 1–50 of { "email": string, "name"?: string }. Any email is accepted — signatories do not need to be Waafir users; external addresses get a guest signing link. |
transactionId | string (UUID) | no | Attach the envelope to a transaction. |
send | boolean | no | When true, open the envelope for signing immediately. Requires confirm_send: true. |
confirm_send | boolean | no | Explicit acknowledgement that send: true immediately emails a legally-binding signature request to every signatory. Must be true whenever send is true; ignored otherwise. |
signingMode | "parallel" | "sequential" | no | Signing order. "parallel" is the default: signatories may sign in any order, and sending emails all of them at once. "sequential" emails only the first, and releases each signatory once every earlier one has signed; signing out of turn is refused with CONFLICT. Omitted — or misspelled signing_mode — means "parallel". |
fieldLayout | array | no | Field-placement layout override; omit for the default layout. |
send: true without confirm_send: true is refused with INVALID_ARGUMENT —
the acknowledgement exists because sending dispatches real, legally-binding
signature-request emails to every listed address the moment the call succeeds.
Returns the envelope, whose documented fields are id, fileId, status,
signingMode, createdBy, signedOutputName, signedOutputVersionCount,
fieldLayout, packetPageCount and expiresAt. The response currently also
carries internal storage identifiers that are not part of the contract —
do not read or persist them; they may disappear without notice.
A draft envelope must be sent before anyone can sign. Sending consumes one
envelope from your monthly quota — at the ceiling, a send returns
QUOTA_EXCEEDED and the envelope stays a draft you can send later. A mis-sent
envelope can be recalled with esign_void_envelope.
esign_list_envelopes (read)
List your organisation's envelopes, newest first, with each envelope's
signatories and statuses. Optional filters: transactionId, fileId — both
camelCase. Because they are optional, a snake_case spelling is dropped
silently and the call returns the unfiltered list rather than an error;
see the key note above.
Paginated — limit (optional, 1–100, default 25) and offset (optional,
default 0) are clamped rather than rejected. Returns:
{ envelopes: [...],
pagination: { limit, offset, count, has_more, next_offset, total },
filtered_by_pat_scope: boolean }Use has_more to tell a clipped result from a complete one, and follow
next_offset until it is null; never infer more-pages from
count === limit. total is the number of envelopes this token can see.
filtered_by_pat_scope is true when the token is restricted to specific data
rooms and that allow-list constrained the result — the window is applied after
the filter, so pages are always exact. It is deliberately a boolean and not a
count of hidden envelopes: reporting how many were withheld would leak the
existence and volume of envelopes the token was restricted from seeing.
esign_get_envelope (read)
Get one envelope with its signatories and their statuses
(pending / signed / declined). Input: envelopeId. The envelope
additionally carries transactionId, createdAt, updatedAt, and
signatories (each { id, email, name, status, signedAt }).
esign_sign (write)
Sign your own pending signatory slot — the slot whose email matches the user who owns the token. Signing does not consume quota (the envelope was already metered when it was sent).
| Field | Type | Required | Description |
|---|---|---|---|
envelopeId | string (UUID) | yes | The envelope to sign. |
consentAffirmed | boolean | yes | Must be literally true — an explicit affirmation of the consent statement. There is no default; omitting it, or passing false, fails the call. |
fields | object | no | Fill values keyed by field label; name and date default when omitted. |
Returns { envelope }.
Errors you may see: FORBIDDEN if you are not a pending signatory on the
envelope, CONFLICT if the envelope is not open for signing — or, on a
sequential envelope, if an earlier signatory has not signed yet. These are
in-payload error.code values, not HTTP statuses — see
Setup.
One caller-fixable failure currently arrives as INTERNAL. If the
envelope's layout has required fields and fields does not supply them, the
refusal is a 422, and 422 has no entry in this surface's status-to-code map —
so it surfaces as INTERNAL, the one code the table tells you not to retry,
with a generic message rather than the missing labels. It is retryable once
you fill the fields. Until that mapping is fixed, do not treat an INTERNAL
from esign_sign as unfixable without first checking the layout's required
fields; the in-app signing screen names them.
esign_decline (write)
Decline your own pending slot (terminal — the envelope becomes declined).
Input: envelopeId. Returns { id, status: "declined" }. Consumes no quota.
esign_void_envelope (write)
Recall a mis-sent envelope. This is the remedy when a signature request went out that should not have: the envelope stops accepting signatures, every outstanding guest signing link is invalidated immediately, and pending signatories can no longer act.
Input: envelopeId. Returns the voided envelope.
- Terminal. A voided envelope cannot be re-sent — create a new one instead.
- Signatures already recorded and the full audit trail are preserved; voiding does not erase what was already signed.
- Legal only while the envelope is
draft,sent, orpartially_signed. Any other status returnsCONFLICT, and an envelope that has expired reports that rather than a stale status. - Who may void: only the person who created the signature request, or an
organization owner or admin. Anyone else gets
FORBIDDEN; a caller who is not a party to the envelope at all getsNOT_FOUND, so the tool never confirms an envelope exists to someone who should not know. - Voiding consumes no quota — and refunds none. Quota is charged when an envelope is sent.
esign_list_my_pending_signatures (read)
List the envelopes awaiting your own signature in this organisation: every
open envelope with a pending signatory slot matching the email of the token's
owner. This is the most useful read to make before esign_sign.
No inputs. Returns
{ envelopes: [{ id, fileId, documentName, status, createdAt }] }.
Scoped to this token's organisation and, if the token is restricted, to its data-room allow-list — pending signatures in other organisations are never shown, even though you may be a signatory there.
esign_download_signed (read)
Get a short-lived presigned download URL for the signed PDF. Input:
envelopeId. Returns { download_url, expires_in_seconds } — note both keys
are snake_case here, unlike the rest of this server. The envelope must have at
least one recorded signature; the signed PDF is also available in the product
as the newest version of the source file.
esign_quota_status (read)
No inputs. Returns { quota, used, remaining } for the current calendar
month — total envelope quota (plan bundle + purchased units), envelopes sent,
and headroom before the ceiling.
Legal validity
Signatures are eIDAS Advanced Electronic Signatures (PAdES), valid for NDAs and contracts under US ESIGN/UETA and EU eIDAS. Signer consent (IP, user agent, timestamp) is recorded on Waafir's append-only, hash-chained audit trail at signing time. Envelopes created or sent through this MCP server are recorded on that trail as token-initiated actions, so an agent-initiated send is always distinguishable from one a person performed in the app. Qualified (QES) signatures are not part of this release.
waafir-dataroom reference
Reference for the waafir-dataroom MCP server — all thirty tools for reading and managing data rooms over a Personal Access Token, grouped into read, folder, file, document-text, permission, and Q&A tools, with their inputs, outputs, and required scopes.
Investor permissioning, roles, and lifecycle
How investors get into a Waafir data room, what they can do once in, and how access is granted, scoped, extended, and revoked. Covers the IAM v2 role model, dataroom-level permissions, the share-link and guest-access flow, NDA gating, and the full invite-to-revoke lifecycle.