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.
| Command | What it does |
|---|---|
apisign auth login --api-key <key> | Validate the key and save it |
apisign auth status | Say whether a key is saved and show its first 8 characters. Does not contact the API. |
apisign auth logout | Clear stored credentials |
apisign config | Print the current configuration. --json prints the full API key. |
apisign init | Print 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
| Command | Options | Works in 0.1.1 |
|---|---|---|
template list | --json | Only with --json, which prints { "templates": [...] }. Without it the command fails with a map is not a function error. |
template get <id> | --output <file>, --json | Only 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>, --interactive | Yes, 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 confirmation | No. 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
| Command | Options | Works in 0.1.1 |
|---|---|---|
contract list | --status <status>, --json | Only 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>, --json | Only with --json, which prints { "success": true, "contract": {...} } with the signers inside contract. |
contract create | --template <id>, --name <name>, --interactive | No. 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> | --yes | No, for the same id/contract_id reason. |
contract logs <id> | --json | No. 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.