Skip to content

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.

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’s api_admin with 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 the X-Api-Key above.

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.

  • Burst limit: per-key request throttling. Responses include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. Exceeding it returns 429 with a Retry-After.
  • Monthly quota: each account has a monthly_call_limit (per plan). When exhausted, calls return 429. Monitor usage via the portal or GET {BASE}/usage/summary/.
MethodPathPurpose
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.)

Terminal window
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:

  • typeOfSignature may be E-Signature, aadhaar_digital_signature (if Aadhaar enabled), or roles like Approver / Reviewer. Optional field coordinates (x_cor, y_cor, page_no) and sequence (signing order) are supported.
  • Idempotency: send a unique Idempotency-Key per 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: false in documentDetails and use the returned signing_info URLs, or call /document/signing-links/ later.
Terminal window
# Status
curl "https://sign.example.com/backend/othercompanyapi/document/sign/status/?document_id=84213" \
-H "X-Api-Key: qsk_live_xxxxxxxx"
# Download the signed PDF once complete
curl "https://sign.example.com/backend/othercompanyapi/document/download/?document_id=84213" \
-H "X-Api-Key: qsk_live_xxxxxxxx" -o signed.pdf

Register a URL to receive signing events instead of polling.

  1. POST /webhook/save/ with { "webhook_url": "https://your-app/hooks/qsign" }. The response includes a signing secret (shown once) — store it.
  2. 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, and X-QSign-Event: <event_type>.
  3. 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.test event is available via /webhook/test-event/.
  • Delivery: signed, retried with backoff, and logged. Inspect attempts at /webhook/deliveries/. Your endpoint should respond 2xx quickly 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.
StatusMeaningAction
400Invalid/missing API key or malformed requestCheck the key + payload
403Account suspended or subscription expiredContact the platform operator
429Rate limit or monthly quota exceededHonor Retry-After; check X-RateLimit-* / usage
5xxServer errorRetry with backoff; use the same Idempotency-Key

Always send an Idempotency-Key on create so safe retries don’t double-create.

  • Build/test against a sandbox key first.
  • Store keys + webhook secret in your secret manager; never in client-side code.
  • Verify the X-QSign-Signature HMAC on every webhook.
  • Use Idempotency-Key and handle 429/Retry-After.
  • Promote to a live key only after the sandbox flow works end-to-end.