waafir-dataroom reference
The waafir-dataroom server is the stable API surface of this release. It
exposes thirty tools for reading and managing your data rooms, grouped by
what they do: six read tools, five folder tools, nine file tools (seven
file-lifecycle tools plus two file-version tools), one document-text tool, seven
permission tools, and two Q&A tools.
Connection: https://app.waafir.io/api/mcp/waafir-dataroom — see
Setup.
How access is enforced
Every tool runs as the token's owner and is bounded by the same rules the product UI enforces — there is no looser path through MCP. Two gates apply on every call:
- Scope. Each tool states a minimum scope:
read tools require
read, folder and file mutations requirewrite, and the permission-granting tools requireadmin. A token below the required scope is rejected with aFORBIDDENerror (see How a failure is reported). Scope is cumulative —writeincludesread,adminincludes both. - Data room scoping. If the token is restricted to specific data rooms, any call referencing a data room outside its allow-list is rejected, and a scoped token cannot even list the data rooms it is not allowed to open. The check is keyed on the data room a resource belongs to, so a token scoped to data room A can never reach a file or folder in data room B — even within the same organization.
In addition, folder and file mutations require a deal-team role for the data room (investors cannot mutate), and the permission tools additionally require the token owner to be an organization admin or manager. Deleting a folder or file is always a soft delete — rows are marked deleted and preserved for retention and audit, never hard-removed. Every mutation is recorded as the same audit event the product UI writes, so an MCP action is indistinguishable from a UI one in the audit log.
Identifiers (dataroom_id, folder_id, file_id, user_id) are UUIDs.
How a failure is reported
A tool failure is not an HTTP status. When a tool refuses a call, the HTTP
response is still 200 and the JSON-RPC envelope is still a success
envelope; the failure is carried inside the result, which sets
isError: true and whose text content is:
{ "error": { "code": "CONFLICT", "message": "…" } }Branch on error.code, never on the HTTP status — a client that switches on
status will never match any refusal on this page. These are the codes a data
room tool returns:
| Code | Meaning | What to do |
|---|---|---|
INVALID_ARGUMENT | An argument is missing, malformed, or not a valid id | Fix the arguments and retry |
FORBIDDEN | The token is valid but not permitted — scope too low, outside its data-room allow-list, or the owner's role does not allow it | Ask for access, or use a token with the right scope |
NOT_FOUND | No such record, or it is not yours | Check the id |
CONFLICT | Understood and permitted, but the data room is not in a state where it can be done | Read the message, fix the blocker, retry |
TOO_LARGE | The input exceeds a size limit | Split the input |
QUOTA_EXCEEDED | A metered allowance is spent, or the organization's subscription has lapsed | Somebody must buy or upgrade — retrying will not clear it |
UPSTREAM_ERROR | A downstream service failed — the stored document could not be fetched, or text extraction failed on it | Transient. Retry later before treating it as fatal |
UNAVAILABLE | The read could not be recorded to the audit log, so the content was withheld rather than disclosed unlogged | Transient. Retry later |
INTERNAL | An unexpected failure | Do not retry — escalate |
Do not collapse the two retry-later codes into "failed". UPSTREAM_ERROR
and UNAVAILABLE reach you today from get_file_text, and they mean ask
again, not give up. A client that treats every non-OK result as terminal
turns a passing blip into a permanent failure — the one mistake this table
exists to prevent. Note also that UNAVAILABLE is a deliberate refusal:
Waafir would rather withhold a document's text than hand it over without an
audit record.
The message names the blocking record wherever there is one. Transport-level
failures — an unknown server name, a missing or revoked token, an unpurchased
paid add-on — are real HTTP statuses and are listed in
Setup.
Pagination
No list tool returns an unbounded array. Every tool that returns a list
accepts an optional limit and offset, and returns a pagination object
alongside the rows:
| Field | Meaning |
|---|---|
limit | the page size actually applied |
offset | the offset actually applied |
count | how many rows are on this page |
has_more | true when more rows exist beyond this page |
next_offset | the offset to pass for the next page, or null when the result is complete |
total | the full number of rows behind the window — present only where it is cheap to compute |
limit and offset are clamped, not rejected: a value above the maximum
comes back as the maximum, a value below the minimum as the minimum, and an
absent or non-numeric value as the documented default. The pagination block
always reports the values that were actually applied, so you can see what you
got rather than what you asked for.
Use has_more to decide whether a result is complete — never infer it from
count === limit, which is ambiguous on an exactly-full final page. To read a
whole list, follow next_offset until has_more is false.
Read tools
Require read scope.
list_datarooms
List the data rooms the caller can access. For a token scoped to specific data rooms, the result is filtered to that allow-list. Archived data rooms are excluded.
- Inputs:
limit(optional, 1–200, default 50);offset(optional, default 0). - Output:
{ datarooms: [{ id, name, description, org_name, created_at }], pagination: { limit, offset, count, has_more, next_offset, total } }. - Ordered oldest-first by
created_at, with an id tiebreak so pages are stable.totalis the full number of data rooms this token can reach, counted after the allow-list filter is applied.
get_dataroom
Get a single data room by id. The caller must be authorized for it; a token scoped to other data rooms is rejected.
- Inputs:
dataroom_id(required). - Output:
{ dataroom: { id, name } | null }.
list_folders
List folders in a data room. Honors deal-team vs investor visibility — an
investor only sees folders they may read. List one level under a parent, or pass
all for the whole tree. Deleted folders are excluded.
- Inputs:
dataroom_id(required);parent_folder_id(optional — omit for top-level folders);all(optional boolean — true lists every folder in the data room);limit(optional, 1–500, default 100);offset(optional, default 0). - Output:
{ folders: [{ id, name, parent_folder_id, created_at }], pagination: { limit, offset, count, has_more, next_offset, total } }. - Sorted by name, with an id tiebreak so pages are stable.
all: trueis paginated too — on a large tree, page through withnext_offsetrather than expecting the whole tree in one response.
list_files
List active (non-deleted) files in a data room, optionally within a folder. Investor callers only see files they are permitted to read. Paginated.
- Inputs:
dataroom_id(required);folder_id(optional — omit for files at the data room root);limit(optional, 1–200, default 50);offset(optional, default 0);sort_dir(optional,asc|desc, defaultdesc). - Output:
{ files: [{ id, name, folder_id, size_bytes, mime_type, created_at, updated_at }], pagination: { limit, offset, count, has_more, next_offset } }. - No
total— counting the full match set is not cheap on this path. Usehas_more/next_offsetto page.
get_file_metadata
Get metadata for a single file: name, MIME type, size, folder path, version list, and AI summary/category if present. The file must belong to the named data room; a file in another data room (even the same org) returns null.
- Inputs:
dataroom_id(required);file_id(required). - Output:
{ file: { id, name, mime_type, size_bytes, folder_id, folder_path, created_at, updated_at } | null, versions: [{ id, version_number, is_current, created_at }], ai_metadata: { summary, key_points, category, is_redacted } | null }.
search_dataroom
Search files by name within a single authorized data room (case-insensitive substring match). Deleted files are excluded.
- Inputs:
dataroom_id(required);query(required, filename substring);limit(optional, 1–100, default 25);offset(optional, default 0). - Output:
{ files: [{ id, name, mime_type, size_bytes, created_at }], pagination: { limit, offset, count, has_more, next_offset } }. - Newest first, with an id tiebreak so pages are stable. When
has_moreistruethe query matched more files than were returned — page withnext_offset, or narrow the query. There is nototal.
Folder tools
Require write scope and a deal-team role for the data room.
create_folder
Create a folder in a data room. Nest it under a parent, or omit the parent for
the data room root. Rejects a duplicate name under the same parent with a
CONFLICT.
- Inputs:
dataroom_id(required);name(required, 1–255 chars);parent_folder_id(optional — null/omitted for the data room root). - Output:
{ folder: { id, name, parent_folder_id } }. TheCONFLICTmessage names the conflicting folder's id, so you do not need to re-list the parent to find it.
rename_folder
Rename a folder. The folder must belong to the caller's organization.
- Inputs:
folder_id(required);name(required, 1–255 chars). - Output:
{ folder: { id, name } }.
delete_folder
Soft-delete a folder and its entire subtree — every nested sub-folder and
every file inside them. On a real data room that can be hundreds of documents
from a single folder id, so the result reports exactly how many folders and files
were removed. Pass dry_run: true to get those counts without deleting anything.
Rows are marked deleted, never hard-removed, and the whole cascade can be undone
with restore_folder.
- Inputs:
folder_id(required);dry_run(optional boolean — preview only). - Output:
{ deleted, dry_run, folder_id, folder_name, affected: { folders, files }, restore_with: "restore_folder" }. - A dry run changes nothing and records no deletion in the audit trail.
restore_folder
Undo a delete_folder. Restores the folder and everything that delete removed
with it — anything deleted separately, earlier, stays deleted. Returns a conflict
if the parent folder is still deleted (restore that first) or if a live folder has
since taken the name; in both cases the message names the blocking folder's id.
- Inputs:
folder_id(required);dry_run(optional boolean — preview only). - Output:
{ restored, dry_run, folder_id, folder_name, affected: { folders, files } }. - A folder delete is lossless to undo, and deliberately unlike a file delete.
The cascade does not move your organization's storage counter and does not drop
the files' search-index chunks, so
restore_foldercredits nothing back — nothing was debited — and the restored files are still searchable.delete_file/restore_filebelow do both. Deleting a folder full of documents is therefore not the same accounting event as deleting each of those documents.
move_folder
Reparent a folder within the same data room. Nest it under another folder, or move it to the data room root. Rejects moving a folder into itself or a descendant (cycle), into a different data room, or where a sibling with the same name already exists.
- Inputs:
folder_id(required);new_parent_folder_id(optional — null/omitted for the data room root). - Output:
{ folder: { id, name, parent_folder_id, previous_parent_folder_id } }. Passprevious_parent_folder_idback asnew_parent_folder_idto undo the move.
File tools
File mutations require write scope; get_file_download_url requires only
read.
Uploading is brokered, and MCP never carries the bytes. Tool arguments are
JSON, so the protocol has no binary channel: request_file_upload mints a
presigned URL and your client PUTs the content to it directly. A client that
cannot issue an HTTP PUT with a raw binary body cannot complete an upload at all
— worth knowing before you start rather than after.
Which tool finalises the upload depends on whether a file of that name is already
there, and you do not have to work it out: request_file_upload returns a
next_step field naming the tool to call.
request_file_upload returns | Meaning | Finalise with |
|---|---|---|
is_new_version: false, next_step: "register_file" | No same-name active file — a pending record was created at version 1 | register_file |
is_new_version: true, next_step: "finalise_file_version" | A same-name active file exists — this upload becomes a new version of it | finalise_file_version |
Call the tool next_step names. register_file only ever finalises version 1 and
rejects anything else, so reaching for it on the new-version branch fails the call.
request_file_upload
Request a presigned upload URL for a file in a data room. Returns the URL and
headers to PUT the bytes to, the file id, and next_step — the tool that
finalises this particular upload. If a same-name active file already exists in
the target folder, is_new_version is true and the returned id and
version_number target that existing file.
- Scope:
write. - Inputs:
dataroom_id(required);file_name(required, with extension);file_size(required, exact bytes, positive integer);mime_type(required);folder_id(optional — omit for the data room root). - Output:
{ upload_url, headers, file_id, s3_key, is_new_version, version_number, next_step }—next_stepis"register_file"or"finalise_file_version". - This tool moves no bytes; it only brokers the URL. If object storage is not configured the call fails outright rather than handing back a URL that cannot work.
register_file
Finalise a pending first upload after PUTting the bytes from
request_file_upload: marks the file active, increments org storage, and records
version 1. Call it when request_file_upload returned is_new_version: false —
its next_step names this tool.
- Scope:
write. - Inputs:
file_id(required, the pending id fromrequest_file_upload);s3_key(optional);size_bytes(optional — derived from the uploaded object when omitted). - Output:
{ file_id, status: "active", version_number: 1 }. - Version 1 only, and it enforces that. A file that is already active, or that
already carries a version, is rejected — the refusal names
finalise_file_versionas the tool for versions 2 and up. - The storage key is derived server-side and the client's
s3_keyis never trusted. It is optional; if you do send it, it must equal the keyrequest_file_uploadreturned or the call is rejected.
finalise_file_version
Finalise a new version of an existing file, after PUTting the bytes to the URL
request_file_upload returned with is_new_version: true. This is the MCP
equivalent of replacing a document in the UI, and the tool next_step names on
that branch.
- Scope:
write. - Inputs:
file_id(required);version_number(required — theversion_numberrequest_file_uploadreturned). - Output:
{ file_id, version_id, version_number, size_bytes, is_current: true, previous_version_number, previous_version_id, undo_with: "restore_file_version" }. version_numbermust be exactly the next version for this file. It is graded, not guessed: any other value is rejected rather than quietly written, and the storage key is derived server-side from it.- The new version becomes current and the previous one is retained in the
history. Pass
previous_version_idtorestore_file_versionto undo. - A file that has not completed its first upload is rejected — finalise version 1
with
register_filefirst. The call also fails if the bytes are not actually in storage yet, rather than recording a version with nothing behind it.
restore_file_version
Make an earlier version of a file current again — the undo of
finalise_file_version. It moves the current marker and re-points the file at
that version; it does not mint a new version row, so the version history stays
a faithful list of the revisions that were actually uploaded.
- Scope:
write. - Inputs:
file_id(required);version_id(required — must belong to this file;get_file_metadatalists the file's version ids). - Output:
{ file_id, version_id, version_number, already_current, new_version_created: false }. already_current: truemeans the version was already the current one and nothing changed — a no-op, not a failure.- There is no separate list-versions tool:
get_file_metadataalready returns the file's versions with their ids, numbers, and current flag.
rename_file
Rename a file — metadata only. This does not create a new version;
versioning is for content replacement, not relabelling. Names must be unique among
live files in the same folder; a clash returns a CONFLICT naming the existing
file.
- Scope:
write. - Inputs:
file_id(required);name(required, 1–255 chars). - Output:
{ file_id, name, previous_name, changed }. The returnednameis read back from storage, so it is what was actually persisted —changedis false if the stored name did not move.
move_file
Move a file into another folder in the same data room, or to the data room root. Cross-data-room moves are rejected: a data room is a permission boundary, and re-homing content across one would change who can read it. Rejects a move where a live file of the same name already sits in the destination.
- Scope:
write. - Inputs:
file_id(required);folder_id(optional — null/omitted for the data room root). - Output:
{ file_id, name, folder_id, previous_folder_id, moved }. Passprevious_folder_idback asfolder_idto undo the move.
delete_file
Soft-delete a file: sets deleted_at. The result echoes the name of the file that
was actually deleted, so a mistaken id is visible in the response. The file row and
its stored object/versions are preserved for retention and audit — never a hard
delete — and restore_file brings it back.
- Scope:
write. - Inputs:
file_id(required). - Output:
{ file_id, status: "deleted", name, folder_id, restore_with: "restore_file" }. - Storage is credited back only for an upload that actually completed. A file whose bytes landed returns its size to your organization's storage counter. A file still awaiting its upload was never charged for in the first place, so deleting it moves the counter by nothing — the delete is counter-neutral, not a refund of bytes you never stored.
- Deleting also drops the file's search-index chunks, so the document leaves
document search immediately.
restore_filecannot bring those back — see below. - The tool only ever resolves a live file, so calling it a second time on the
same id returns
NOT_FOUNDrather than deleting anything again. The storage counter cannot be debited twice for one file.
restore_file
Undo a delete_file: bring a soft-deleted file back to live, restoring the row
and the storage accounting that delete reversed. It does not restore the
file's search-index chunks — those are dropped on delete — so the file is live and
downloadable but must be re-indexed before document search finds it again.
search_index_restored is always false and says so rather than implying a
lossless undo.
- Scope:
write. - Inputs:
file_id(required). - Output:
{ restored, file_id, name, folder_id, storage_bytes_restored, search_index_restored: false }.
When a restore is refused. It fails rather than papering over a conflict, and names the blocking row where there is one:
| # | Condition | error.code | What to do |
|---|---|---|---|
| 1 | No such file, or not yours | NOT_FOUND | Check the id. |
| 2 | The file is not deleted — it is already live | CONFLICT | Nothing to restore. |
| 3 | The file's upload never completed | CONFLICT | Nothing to restore: its bytes never landed, so there is no document to bring back. Upload it again instead. |
| 4 | The containing folder is still deleted | CONFLICT | restore_folder first — that also brings back the files deleted with it. |
| 5 | A live file has since taken the name in that folder | CONFLICT | Rename the file that holds the name, then retry. |
These are in-payload codes, not HTTP statuses — see How a failure is reported.
The checks run in the order numbered above, and you are told about the first one that fails. The upload-never-completed case (3) is therefore reported after the not-found and not-deleted checks but ahead of the folder and name checks: a file whose bytes never arrived cannot be restored whatever its folder or name situation, so there is no point sending you to fix either.
get_file_download_url
Get a short-lived presigned download URL for a file. The caller must be authorized for the file (read scope plus the per-file read/download permission). The URL expires after a few minutes and returns the raw file (no watermark burn-in).
- Scope:
read. - Inputs:
file_id(required). - Output:
{ download_url, expires_in_seconds }.
Document text tools
One tool, for reading a document's extracted text rather than downloading the file itself.
get_file_text
Read a document's extracted text, one page at a time. Returns numbered segments
— for a PDF, one segment per page unless the page is very long — plus a
pagination block, so you can walk a long document with offset.
Two things make this stricter than a metadata read:
- You must be permitted to download the file, not merely view it. View-only permission is not enough, and a token restricted away from the document is refused.
- If the document has been redacted and you are not on the deal team, you
receive the text of the redacted version, and
sourcesays which artifact you got. It never silently hands a perimeter reader the original.
Every successful call is written to the organization's audit log, on the same footing as a document read in the product.
- Scope:
read. - Inputs:
file_id(required);dataroom_id(optional — validated against the file's own data room when supplied, and a mismatch reads asNOT_FOUNDrather than confirming the file exists elsewhere);limit(optional, 1–5, default 3);offset(optional, default 0). - Output:
{ file_id, file_name, source, segment_max_chars, segments, pagination }.sourceis"original"or"redacted".paginationcarries atotal. - A file with no stored object — a note, whose body is held inline — returns
NOT_FOUNDwith "no extractable content". - This is the tool that produces the two retry-later codes:
UPSTREAM_ERRORwhen the stored document cannot be fetched or text extraction fails on it, andUNAVAILABLEwhen the audit write for the read fails — in which case the text is withheld deliberately rather than disclosed unrecorded. Both are worth one retry before you treat the document as unreadable.
Permission tools
These read and mutate who can see what. The three read tools require only
read scope (the two directory tools additionally require a deal-team role); the
four granting/revoking tools require admin scope and a token owner who is an
organization admin or manager.
get_file_permissions
List the permission grants on a file (direct and folder-inherited). Mirrors the UI's file-permissions view.
- Scope:
read. - Inputs:
dataroom_id(required);file_id(required);limit(optional, 1–200, default 50);offset(optional, default 0). - Output:
{ permissions: [{ user_id, can_read, can_download, access_revoked, expires_at, granted_by, created_at, source, source_folder_name?, source_folder_id?, user_email?, user_name?, granted_by_email? }], pagination: { limit, offset, count, has_more, next_offset, total }, identities_visible }(sourceis"direct"or"inherited"). - Grants are sorted on
created_at, thenuser_id, thensource, thensource_folder_id, so pages are stable across calls.totalis the full grant count on the file. - The identity fields (
user_email,user_name,granted_by_email) follow the same visibility rule as the directory tools below: they appear for deal-team callers, investor identities additionally require an organization admin or manager, and where you are not permitted the keys are absent rather thannull.identities_visibletells you which case you are in; the grants themselves are always listed.
list_dataroom_users
List the users who hold an active access grant on a data room, with the user_id
the permission tools require plus each grantee's email address and name. This is
usually the first call when you need a user_id.
- Scope:
read, and a deal-team role in the data room's organization (the same gate as the team page). - Inputs:
dataroom_id(required);limit(optional, 1–200, default 50);offset(optional, default 0). - Output:
{ users: [{ user_id, email, name, is_deal_team, org_role, permission, granted_at, expires_at }], pagination: { limit, offset, count, has_more, next_offset, total }, investor_rows_visible }. - Investor identities are omitted unless you are an organization admin or
manager;
investor_rows_visiblereports which set you received. - Lists explicit grantees only. Organization owners and admins have implicit
access without a grant row and will not appear — resolve those with
find_user_by_email.
find_user_by_email
Resolve an email address to the user_id that the permission tools require,
along with the user's organization role and current permission in this data room.
- Scope:
read, and a deal-team role in the data room's organization. Resolving an investor additionally requires an organization admin or manager. - Inputs:
dataroom_id(required);email(required, matched case-insensitively). - Output:
{ found: true, user: { user_id, email, name, org_role, dataroom_permission } }or{ found: false }. Adataroom_permissionofnullmeans the user is in the organization but has no access to this room yet — invite them first. - This is not a global directory lookup: only users inside the data room's
own organization, or holding live access to it, resolve. An unknown address, a
user outside your tenancy, and a user you are not permitted to see all return
an identical
{ found: false }, so a negative answer never reveals whether an account exists.
set_file_permissions
Grant or update a user's read/download permission on a file. A read/write
token, or an owner who is not an org admin or manager, is rejected.
- Scope:
admin. - Inputs:
dataroom_id(required);file_id(required);user_id(required);can_read(required boolean);can_download(required boolean);expires_at(optional ISO timestamp, must be in the future). - Output:
{ success: true, effective: { can_read, can_download } }— the post-grant decision, which reflects file restrictions and folder inheritance and may therefore differ from what you requested. - This tool grants access; it cannot remove it. Passing
can_read: falsewrites an active grant with read denied, which other grants (organization membership, a data-room grant, folder inheritance) may still override. To actually remove a user's access to a file, userevoke_file_permission.
revoke_file_permission
Remove a user's direct permission grant on a file (soft-revoke: the row is
preserved for the audit trail). Returns revoked: false if the user had no
direct grant on the file.
- Scope:
admin. - Inputs:
dataroom_id(required);file_id(required);user_id(required). - Output:
{ success: true, revoked }. - Affects the direct grant only. Access inherited from a folder permission is
unaffected and must be revoked on the folder. Reversible by re-granting with
set_file_permissions.
invite_user_to_dataroom
Grant a known user data-room-level access at a given permission level — a direct access grant (not an email invite). Idempotent (upsert).
- Scope:
admin. - Inputs:
dataroom_id(required);user_id(required);permission(required — one ofviewer,downloader,contributor,manager);expires_at(optional ISO timestamp, must be in the future). - Output:
{ success: true, grant_id }.
remove_user_from_dataroom
Revoke a user's data-room-level access (soft-revoke: sets revoked_at,
preserving the audit trail). Returns revoked: false if the user had no active
grant.
- Scope:
admin. - Inputs:
dataroom_id(required);user_id(required). - Output:
{ success: true, revoked }.
Q&A tools
Two read-only tools over the investor Q&A threads in a data room. There is deliberately no write tool here: an AI client can read and summarise a Q&A thread, but it cannot post a question or publish an answer in your name.
What a call returns depends on who owns the token:
- a deal-team member sees every thread in the data room, including the asker's email address;
- an investor sees only the questions they asked themselves, and no email addresses at all.
Attached documents come back as id and name only — never content, never a download link — and the name of a document the caller may not see is withheld. Use the document tools for content; they apply their own access checks. Org-wide Company Q&A questions, which are not attached to a data room, are never returned by either tool.
list_questions
List the Q&A threads in one authorized data room, newest first, each with its answers.
- Scope:
read. - Inputs:
dataroom_id(required);status(optional —open,answeredorclosed);file_id(optional — only questions asked about that document);limit(optional, 1–100, default 25);offset(optional, default 0). - Output:
{ questions, pagination }. Page withoffsetwhilepagination.has_moreistrue.
get_question
Get one Q&A thread by id, with all of its answers.
- Scope:
read. - Inputs:
dataroom_id(required);question_id(required). - Output:
{ question }. - A question in another data room or another organization — or, for an
investor, one asked by somebody else — reports
NOT_FOUND. The tool never confirms that such an id exists.
Each question carries { id, dataroom_id, title, body, question_type, status, asked_by, file_id, file_name, created_at, updated_at, responses }, plus
asker_email only where the caller is entitled to it — for an investor the
key is absent from the payload rather than present and null. Each entry in
responses is { id, body, responded_by, is_ai_draft, created_at, updated_at, attachments }.
waafir-translate reference
Reference for the waafir-translate MCP server — translate short text synchronously with translate_text, or translate a whole document in a data room with translate_file and translate_file_status.
waafir-esign reference
Reference for the waafir-esign MCP server — all nine tools to create e-signature envelopes over your organisation's files, collect legally-valid AES (PAdES) signatures from your AI workflows, track signing status, recall a mis-sent envelope, and download signed documents. Metered against your plan's monthly envelope quota.