API integration guide
Everything below works against the sandbox with a test_ key — no sales call, no waiting.
Overview
The Signatta API lets your app send documents for signature, track ceremonies, pull tamper-evident evidence, and receive webhooks — without building any of it. Base URL: https://api.signatta.example.com/api/v1. Everything is JSON; IDs are ULIDs.
Three integration styles: (1) full API — your backend drives envelopes; (2) embedded signing — open our ceremony in your WebView; (3) webhooks-only — get notified when things happen elsewhere.
Authentication
Create an API key in Dashboard → API keys (test_ for sandbox, live_ for production). Send it as a Bearer token. Keys are hashed at rest, shown once at mint time, and revocable instantly. Keep live keys server-side — never ship them in mobile apps or browsers.
Health check
curl https://api.signatta.example.com/api/v1/health
Authenticated call
curl https://api.signatta.example.com/api/v1/envelopes \ -H "Authorization: Bearer test_YOUR_KEY"
Quickstart: first envelope in 5 minutes
Create a draft, upload the PDF, add a signer, send. The signer gets an email/SMS with a personal link — no Signatta account needed on their side.
1. Create a draft (idempotent)
curl -X POST https://api.signatta.example.com/api/v1/envelopes \
-H "Authorization: Bearer test_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"title":"Employment contract — Maria","doc_category":"employment","routing":"parallel"}'2. Upload the PDF
curl -X POST https://api.signatta.example.com/api/v1/envelopes/{id}/documents \
-H "Authorization: Bearer test_YOUR_KEY" \
-F "file=@contract.pdf;type=application/pdf"3. Add a signer
curl -X POST https://api.signatta.example.com/api/v1/envelopes/{id}/recipients \
-H "Authorization: Bearer test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Maria Silva","email":"maria@example.com","phone":"+351910000000","role":"signer","identify_level":"sms_otp"}'4. Send
curl -X POST https://api.signatta.example.com/api/v1/envelopes/{id}/send \
-H "Authorization: Bearer test_YOUR_KEY"Client examples
The complete envelope flow — draft, upload, signer, send — in your language. Copy, paste a test_ key, run. Uploads are multipart; everything else is JSON. Always send Idempotency-Key on creation calls.
Full flow
KEY=test_YOUR_KEY
IDEM=$(uuidgen)
ENV=$(curl -s -X POST https://api.signatta.example.com/api/v1/envelopes \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEM" \
-d '{"title":"Employment contract — Maria","doc_category":"employment","routing":"parallel"}' | jq -r .id)
curl -s -X POST https://api.signatta.example.com/api/v1/envelopes/$ENV/documents \
-H "Authorization: Bearer $KEY" \
-F "file=@contract.pdf;type=application/pdf" | jq .id
curl -s -X POST https://api.signatta.example.com/api/v1/envelopes/$ENV/recipients \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"name":"Maria Silva","email":"maria@example.com","phone":"+351910000000","role":"signer","identify_level":"sms_otp"}' | jq .id
curl -s -X POST https://api.signatta.example.com/api/v1/envelopes/$ENV/send \
-H "Authorization: Bearer $KEY" | jq .statusEnvelope lifecycle
draft → sent → partially_signed → completed, with declined, voided, and expired as terminal states. Poll GET /envelopes/{id} or (better) subscribe to webhooks instead. Sending is quota-checked: HTTP 402 with an upgrade_url means the allowance is exhausted — surface it, don't retry blindly.
Documents are immutable once uploaded; template edits create new versions so in-flight envelopes always reference the exact bytes that were signed.
Signing ceremony (no signer account)
Drive the ceremony from your own UI: 1) GET state (?recipient_id=) to learn otp_required; 2) POST challenge to text a code; 3) POST challenge/verify to get a single-use challenge_token (30 min); 4) POST sign with the token plus your own idempotency key. A 403 means the token burned or expired — restart at step 2, never retry sign with the same token.
Request → verify → sign
curl -X POST .../envelopes/{id}/challenge?recipient_id={rid} \
-H "Authorization: Bearer test_YOUR_KEY" -d '{}'
curl -X POST .../envelopes/{id}/challenge/verify?recipient_id={rid} \
-H "Content-Type: application/json" -d '{"recipient_id":"{rid}","code":"123456"}'
curl -X POST .../envelopes/{id}/sign?recipient_id={rid} \
-H "Content-Type: application/json" \
-d '{"recipient_id":"{rid}","document_id":"{doc}","full_name":"Maria Silva","agreed":true,"idempotency_key":"<uuid>","challenge_token":"<token>"}'Embedded signing (WebViews & kiosks)
POST /signing-sessions with a recipient_id to mint a single-use URL (15 min TTL). Open it in your WebView or kiosk browser. Replaying a consumed URL returns the completed state — never a second signature. Ideal for offline-capable front desks: queue the session creation, never the signing tap.
Evidence & verification
GET /envelopes/{id}/evidence returns the full pack: documents with SHA-256 hashes, per-signer methods and timestamps, and the hash-chained audit trail. GET /verify/{code} is public and rate-limited — embed it in your own UI or print it on paper copies. Completed envelopes expose the sealed final PDF for download; every download is itself audited.
Webhooks
Subscribe URLs to events: envelope.sent/signed/completed/declined/voided/expired, recipient.viewed/signed, bulk_batch.finished, timestamp.renewed. Every payload carries X-Signatta-Signature (HMAC-SHA256 over the raw body with your endpoint secret). Verify before trusting — then return 2xx fast; we retry 1m → 5m → 15m before dead-lettering visibly in your dashboard.
Verify signature
import crypto from "node:crypto";
function valid(rawBody, signature, secret) {
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}Templates & bulk
POST /templates with a master PDF and role list (employer, employee) to get a versioned template. POST /templates/{id}/use with per-role signers to spawn a draft. For volume, POST /bulk-batches with template_version_id, fixed_signers, and a CSV (name,email,phone — 200 rows max). The batch runs async in our queue; poll GET /bulk-batches/{id} for sent/failed counts with per-row errors.
Idempotency, limits & errors
Creation endpoints REQUIRE an Idempotency-Key header (24h window): retries return the original response byte-for-byte, never a duplicate. Status codes: 401 bad key, 403 wrong role or burned ceremony token, 404 cross-org reads, 409 double-sign, 422 validation, 429 rate/OTP lockout, 402 quota with upgrade_url. Test keys hit the full stack with a fake timestamp authority and trapped mail — integrate without spending envelopes.
Support & SLA
Sandbox is free forever. Email support on all plans; scale plans get defined response times. Status and incidents are published on the status page. When something looks wrong, send us the envelope ID and verification code — everything is traceable from there.