CLI

apisign-io drives the same REST API from a terminal. It is a single bundled file with no dependencies of its own, so npx fetches about 270 KB and runs. The current release is 0.1.1.

0.1.1 only partly works

Several commands send requests the API rejects, or misread what it returns. The tables below say which work in 0.1.1. For the rest — creating, sending, cancelling, and deleting contracts — use the REST API, the dashboard, or the MCP server.


Install

Run it without installing anything:

npx apisign-io --help

Or install it globally so the apisign command is always on your path:

npm install -g apisign-io
bun add -g apisign-io

Requires Node 18 or newer.


Authenticate

apisign auth login --api-key YOUR_API_KEY

The key is checked against the API and saved only if it works. There is no browser login: apisign auth login without --api-key, or with --browser, prints Browser login is not available yet. and exits.

Create keys in the dashboard under Account > MCP & API Keys. The CLI calls REST routes, which do not check a key's permission, so a Read Only key can do everything a Read/Write key can.

CommandWhat it does
apisign auth login --api-key <key>Validate the key and save it
apisign auth statusSay whether a key is saved and show its first 8 characters. Does not contact the API.
apisign auth logoutClear stored credentials
apisign configPrint the current configuration. --json prints the full API key.
apisign initPrint setup instructions

Credentials live in ~/.apisign/config.json. Requests go to https://apisign.io unless that file sets apiUrl or the WAKU_PUBLIC_API_URL environment variable is set.


Templates

apisign template list --json
apisign template get TEMPLATE_ID --json
apisign template upload ./nda.docx
CommandOptionsWorks in 0.1.1
template list--jsonOnly with --json, which prints { "templates": [...] }. Without it the command fails with a map is not a function error.
template get <id>--output <file>, --jsonOnly with --json, which prints { "template": {...} }. The plain view shows undefined fields, and --output without --json writes an empty file.
template create--file <file>, --name <name>, --interactiveYes, though it prints Template ID: undefined; find the ID with template list --json. The name must be at least 3 characters.
template update <id>--file <file>, --name <name>Only with both --name and --file; the API requires both.
template upload <file>--name <name>.docx files only, and only without --name: the name is sent as the filename, which must end in .docx. Prints Template ID: undefined.
template delete <id>--yes to skip the confirmationNo. The API rejects the request and nothing is deleted. Archive templates in the dashboard or with the MCP template_archive tool.

template also answers to templates, and list to ls.


Contracts

CommandOptionsWorks in 0.1.1
contract list--status <status>, --jsonOnly as contract list --json, which prints { "contracts": [...] } with id, name, status, created_at, and test_mode. The table view and --status both fail.
contract get <id>--output <file>, --jsonOnly with --json, which prints { "success": true, "contract": {...} } with the signers inside contract.
contract create--template <id>, --name <name>, --interactiveNo. It sends no signers and calls the template templateId instead of template_id, so the API answers Invalid request data.
contract send <id>--email <emails...>, --message <message>No. It sends id where the API expects contract_id, so nothing is sent or charged. The API also takes signers from the contract, not from --email, and has no custom message.
contract cancel <id>--reason <reason>No, for the same id/contract_id reason. It also always asks for confirmation; there is no --yes.
contract delete <id>--yesNo, for the same id/contract_id reason.
contract logs <id>--jsonNo. It always reports no logs, because it reads a field the API does not return.

contract also answers to contracts, and list to ls.


Scripting

Every read command takes --json, and in 0.1.1 that is also the only way the list and get commands work. The output is the API's response body, so lists sit under a key rather than at the top level:

# Every contract still waiting on a signature
apisign contract list --json \
  | jq -r '.contracts[] | select(.status == "sent") | "\(.id)  \(.name)"'

For an agent rather than a shell, use the MCP server, where creating, sending, and cancelling contracts all work as tools.