@zsign/cli
v0.1.1
Published
Command-line client for zSign — lint, send, and track e-signature envelopes.
Downloads
332
Readme
@zsign/cli
A thin command-line client for zSign — lint a PDF's signature fields, send it for signature, and track envelopes through completion, all from a terminal or a script.
Zero runtime dependencies. Requires Node 18+.
For a task-oriented walkthrough, start with the zSign CLI guide. This file is the complete command and behavior reference.
Install
npx @zsign/cli <command> ...Or install it once:
npm install -g @zsign/cli
zsign <command> ...Getting started
zsign initinit creates an account (or, with --email/--referral-code, attaches one)
and writes an API key to ~/.zsign/config.json (mode 0600), alongside the
base URL it was issued for. Every later command reads that file automatically
— nothing else to configure.
To use an existing account instead of creating one, skip init and set the
key yourself:
export ZSIGN_API_KEY=zs_live_...
zsign creditsCredential resolution order (highest priority first): --api-key flag,
ZSIGN_API_KEY env var, the stored config file. --base-url /
ZSIGN_BASE_URL resolve the same way, and default to https://zsign.io.
Self-hosting
Point the CLI at your own deployment with --base-url (or ZSIGN_BASE_URL):
zsign init --base-url https://zsign.your-domain.comA stored key is only ever sent to the base URL it was issued for. Point the
CLI anywhere else — --base-url, ZSIGN_BASE_URL — and the stored key is
withheld: that call runs unauthenticated unless you pass a key explicitly
with --api-key or ZSIGN_API_KEY. So neither a self-hosted credential nor
a zsign.io one can be handed to a host you merely pointed at.
Plaintext http:// is refused unless the host is loopback or --insecure is
passed explicitly.
Commands
init
Create an account and store its API key.
zsign init --email [email protected]lint
Check a PDF's field tags before sending it — no credential required. Reports
every tag found, the parties they belong to, and any syntax errors, exiting
5 if the document would be rejected.
zsign lint contract.pdfThe tag syntax itself is deliberately not restated here: it is defined in
one place and served from it, at zsign.io/docs/api
and GET /api/v1/field-syntax. Read it there so this page cannot drift out of
date underneath you.
send
Send a PDF to one or more parties for signature. Each --to maps a party
name (as tagged in the PDF) to a recipient.
zsign send contract.pdf --to "client=Alice <[email protected]>"status
Check an envelope's status, or follow it live with --watch.
zsign status <document-id> --watch--watch polls every 2 seconds; --interval <seconds> overrides it, accepting
1 to 3600.
fields
List a document's fields and their current values (unanswered fields report
null, they aren't omitted).
zsign fields <document-id>void
Cancel an in-progress envelope.
zsign void <document-id> --reason "sent by mistake"download
Download the completed, signed PDF. Accepts either the original document id or the completed one — the CLI resolves it.
zsign download <document-id> --output signed.pdfPass --stdout instead of --output to stream the raw PDF bytes to stdout
(cannot be combined with --json).
listen
Stream this account's webhooks to a local URL — no tunnel, no inbound port.
The CLI long-polls a server-side queue and forwards each event to
--forward-to with a real HMAC signature, exactly like a production webhook
delivery.
zsign listen --forward-to http://localhost:3000/hooksThere is one listener per account. A second zsign listen for the same
account from a different machine gets exit code 9 naming the machine that
holds it; pass --replace to take over (this rotates the session secret,
so the displaced listener's events stop verifying). Restarting zsign listen
on the same machine after a crash reattaches to its own session instantly,
without needing --replace.
Ctrl-C detaches the session but does not discard it — new events keep
queueing, and everything already queued or delivered stays around for an
hour, so a short restart doesn't lose anything the way a dropped tunnel
would. Pass --discard-on-exit to delete the session and its retained
events instead.
--retry retries the local handler on the production retry schedule
when it returns a non-2xx — this is unrelated to --no-retry, which governs
retrying zSign's own HTTP 429s. --fail-on-handler-error exits 8 on the
first non-2xx response from your handler, instead of continuing to the next
event.
Each event the CLI receives has its own event id, distinct from the id your production endpoint would get for the same business change — the two are independently delivered and acked copies, not the same delivery twice.
Signature verification is not restated here: it is defined in one place, at zsign.io/docs/webhooks, so this page cannot drift out of date underneath you.
replay
Re-deliver one retained webhook event, e.g. after fixing a bug in your local handler.
zsign replay <event-id> --forward-to http://localhost:3000/hooks--forward-to is mandatory — replay does not remember a target from a
previous listen session, since silently reusing one would be a footgun.
replay neither acquires the listener lease nor changes the event's ack
state, so it's safe to run alongside a zsign listen that's still running.
It exits 8 if your handler rejects the redelivered event.
credits
Check your balance, or buy more. Available packs and prices come from your account, not from this document — run the command to see the current list.
zsign credits
zsign credits buy <pack>help
Print the command list, global flags, and exit codes shown below.
zsign helpNot available yet
preview, whoami, and download --cert are not implemented in this
release. If you need one of these, it's a known gap, not a bug — please
don't file one.
--json
Every command accepts --json for machine-readable output. The envelope
shape is a compatibility contract: fields may be added in a minor
release, but never removed or retyped, so scripts should check for the keys
they need rather than the whole object shape.
Success:
{ "ok": true, "...": "command-specific fields" }Error:
{
"ok": false,
"error": {
"code": "not_found",
"http_status": 404,
"message": "...",
"detail": { }
}
}status --watch and listen always emit newline-delimited JSON (NDJSON) —
one parseable JSON object per line, one line per status transition or
forwarded event — regardless of --json, since both are streams and a
consumer must never have to parse a partially-written array.
Global flags
| Flag | Meaning |
| --- | --- |
| --json | Emit the JSON envelope instead of human-readable text |
| --api-key | API key for this call (overrides stored config / ZSIGN_API_KEY) |
| --base-url | API base URL (overrides stored config / ZSIGN_BASE_URL) |
| --no-retry | Disable automatic retry on HTTP 429 rate limiting |
| --insecure | Allow plaintext http to a non-loopback host |
listen / replay flags
| Flag | Meaning |
| --- | --- |
| --forward-to <url> | Local URL to POST each event to (required) |
| --replace | Take over a session another listener holds |
| --retry | Retry the local handler on the production schedule (not --no-retry, which is about zSign's 429s) |
| --fail-on-handler-error | Exit 8 on the first non-2xx from the local handler |
| --discard-on-exit | Delete the session and its retained events on exit |
Exit codes
| Code | Meaning |
| --- | --- |
| 0 | Success |
| 1 | Internal error |
| 2 | Usage error / bad request (400) |
| 3 | Authentication error (401/403) |
| 4 | Insufficient credits (402) |
| 5 | Validation failed (422) |
| 6 | Not found (404) |
| 7 | Rate limited (429) |
| 8 | Handler failure (listen --fail-on-handler-error, or replay) |
| 9 | Conflict (409) |
| 130 | Interrupted (Ctrl-C) |
