API integration
QSign exposes a white-label REST API so you can create documents, send them for signature, and track/collect results programmatically. This guide is for developers integrating against a deployed QSign instance.
- Base URL:
https://sign.example.com/backend/othercompanyapi - Machine-readable spec:
GET {BASE}/openapi.json(OpenAPI 3.0) — import into Postman / Swagger / Redoc. - Self-service portal: API customers manage keys, usage, webhooks, and try requests at
https://sign.example.com/developers-v2.
Authentication
Section titled “Authentication”The signing API authenticates with an opaque API key sent in the X-Api-Key header.
X-Api-Key: qsk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx- Keys come in two modes: live (
qsk_live_…) and sandbox/test (qsk_test_…). - Only a SHA-256 hash of the key is stored server-side; the full key is shown once at generation — store it securely. Rotating issues a new key and invalidates the old one.
- Generate/rotate keys in the Developer portal, or via
POST {BASE}/apikey/generate/(authenticated as the account’sapi_adminwith a Bearer login token).
Account-management endpoints (key generation, webhooks config, usage) authenticate with the api_admin’s login JWT (
Authorization: Bearer …). The document/signing endpoints authenticate with theX-Api-Keyabove.
Sandbox / test mode
Section titled “Sandbox / test mode”Use a qsk_test_ key to integrate safely: requests return realistic but simulated
responses — no document is created, no emails are sent, no quota is consumed. Switch to a
qsk_live_ key for real signing. Same endpoints, same request/response shapes.
Rate limits & quota
Section titled “Rate limits & quota”- Burst limit: per-key request throttling. Responses include
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset. Exceeding it returns429with aRetry-After. - Monthly quota: each account has a
monthly_call_limit(per plan). When exhausted, calls return429. Monitor usage via the portal orGET {BASE}/usage/summary/.
Core endpoints
Section titled “Core endpoints”| Method | Path | Purpose |
|---|---|---|
POST | /document/recipients/create/ | Create a document and send it for signature |
POST | /document/signing-links/ | (Re)fetch per-recipient signing links (headless) |
GET | /document/sign/status/ | Check a document’s signing status |
GET | /document/download/ | Download the finalized signed PDF |
POST | /apikey/generate/ | Generate/rotate an API key (api_admin JWT) |
GET | /apikey/get/ | Get key prefix + metadata (api_admin JWT) |
POST | /webhook/save/ | Register/update the webhook URL (returns the signing secret) |
GET | /webhook/get/ | Get webhook config |
POST | /webhook/secret/rotate/ | Reveal or rotate the webhook signing secret |
GET | /webhook/deliveries/ | Recent webhook delivery attempts (debugging) |
POST | /webhook/test-event/ | Send a test event to your webhook |
GET | /usage/summary/ | Monthly call usage vs quota |
(The full schema, including request/response fields, is in openapi.json.)
Create & send a document
Section titled “Create & send a document”curl https://sign.example.com/backend/othercompanyapi/document/recipients/create/ \ -X POST \ -H "X-Api-Key: qsk_live_xxxxxxxx" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 4e1c…unique-per-request" \ -d '{ "documentDetails": { "doc_url": "https://files.example.com/nda.pdf", "document_name": "Mutual NDA" }, "recipientDetail": [ { "name": "Jane Doe", "email": "jane@acme.io", "typeOfSignature": "E-Signature", "sequence": 1 } ] }'Response (201):
{ "status": "success", "quoqo_document_id": 84213, "signing_info": [ { "name": "Jane Doe", "email": "jane@acme.io", "document_sign_url": "https://sign.example.com/workflow-api-v2/<token>" } ]}Notes:
typeOfSignaturemay beE-Signature,aadhaar_digital_signature(if Aadhaar enabled), or roles likeApprover/Reviewer. Optional field coordinates (x_cor,y_cor,page_no) andsequence(signing order) are supported.- Idempotency: send a unique
Idempotency-Keyper logical request; retries with the same key return the original result instead of creating a duplicate document. - Headless mode: to deliver the signing links yourself (instead of QSign emailing
signers), set
is_mail_required: falseindocumentDetailsand use the returnedsigning_infoURLs, or call/document/signing-links/later.
Track & download
Section titled “Track & download”# Statuscurl "https://sign.example.com/backend/othercompanyapi/document/sign/status/?document_id=84213" \ -H "X-Api-Key: qsk_live_xxxxxxxx"
# Download the signed PDF once completecurl "https://sign.example.com/backend/othercompanyapi/document/download/?document_id=84213" \ -H "X-Api-Key: qsk_live_xxxxxxxx" -o signed.pdfWebhooks
Section titled “Webhooks”Register a URL to receive signing events instead of polling.
POST /webhook/save/with{ "webhook_url": "https://your-app/hooks/qsign" }. The response includes a signing secret (shown once) — store it.- QSign POSTs JSON events to your URL. Each request carries
X-QSign-Signature: sha256=<hex>= HMAC-SHA256 of the raw request body using your secret, andX-QSign-Event: <event_type>. - Verify every delivery before trusting it:
import hmac, hashlib
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool: expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, signature_header or "")- Events include document signed / declined / completed (see the portal & spec for the
current set). A synthetic
webhook.testevent is available via/webhook/test-event/. - Delivery: signed, retried with backoff, and logged. Inspect attempts at
/webhook/deliveries/. Your endpoint should respond2xxquickly and be idempotent (dedupe on the document id + event type). - Security: the webhook URL is validated to block internal/loopback targets (SSRF
protection); it must be a public
https://endpoint.
Error handling
Section titled “Error handling”| Status | Meaning | Action |
|---|---|---|
400 | Invalid/missing API key or malformed request | Check the key + payload |
403 | Account suspended or subscription expired | Contact the platform operator |
429 | Rate limit or monthly quota exceeded | Honor Retry-After; check X-RateLimit-* / usage |
5xx | Server error | Retry with backoff; use the same Idempotency-Key |
Always send an Idempotency-Key on create so safe retries don’t double-create.
Integration checklist
Section titled “Integration checklist”- Build/test against a sandbox key first.
- Store keys + webhook secret in your secret manager; never in client-side code.
- Verify the
X-QSign-SignatureHMAC on every webhook. - Use
Idempotency-Keyand handle429/Retry-After. - Promote to a live key only after the sandbox flow works end-to-end.