Business plan

API and webhooks

Send documents for signature, track them, download the certified PDF, and receive signed completion events, all over a REST API. Available on the Business plan.

Authentication

Authenticate every request with your API key in the X-DenSign-Key header, or as an ?api_key= query parameter. Treat the key like a password.

curl https://sign.dendat.ai/api/v1/documents \
  -H "X-DenSign-Key: YOUR_API_KEY"
Missing or invalid keys return 401 Unauthorized. The response does not distinguish between the two.

Base URL

https://sign.dendat.ai

Send a document for signature

POST/api/v1/send

Upload a PDF and one or more signers. Fields are detected on upload, each signer is emailed a secure link, and your webhook is called on completion if you registered one. Encoding is multipart/form-data.

FieldRequiredDescription
fileyesThe PDF to be signed
titlenoDocument title, defaults to the filename
signer_nameyesFirst signer's name
signer_emailyesFirst signer's email
messagenoNote included in the signing email
webhook_urlnoURL to POST when the document completes
curl -X POST https://sign.dendat.ai/api/v1/send \
  -H "X-DenSign-Key: YOUR_API_KEY" \
  -F "[email protected]" \
  -F "title=Service Agreement" \
  -F "signer_name=Jane Doe" \
  -F "[email protected]" \
  -F "webhook_url=https://yourapp.com/hooks/densign"

Response 201 Created

{
  "success": true,
  "document_id": 123,
  "status": "sent",
  "signing_url": "https://sign.dendat.ai/sign/<token>"
}

Get a document

GET/api/v1/document/{id}

Returns the document, its status, its signers and its fields. The field map alone is available at GET /api/v1/document/{id}/fields.

Download the signed PDF

GET/api/v1/document/{id}/download

Returns the completed, certified PDF with the certificate page appended. Available once status = completed.

Audit trail

GET/api/v1/document/{id}/audit

The full timestamped log for a document: viewed, signed, declined, completed and every reminder sent. This is the record you produce if a signature is ever questioned.

List documents

GET/api/v1/documents

Paginated list of your documents, filterable by status.

Bulk send

POST/api/v1/bulk-send

Send the same document to many recipients in one call. To nudge everyone still outstanding on a document, use POST /api/v1/document/{id}/bulk-remind.

Webhooks

Set webhook_url when sending. Once every signer completes, DENSIGN posts a JSON event to your URL:

POST https://yourapp.com/hooks/densign
X-DenSign-Event: document.completed
X-DenSign-Signature: t=1720080000,v1=<hmac-sha256-hex>

{
  "id": "evt_...",
  "event": "document.completed",
  "created": 1720080000,
  "document_id": 123,
  "title": "Service Agreement",
  "status": "completed",
  "completed_at": "2026-07-04T09:00:00",
  "signed_file_hash": "3f9a2c...",
  "certificate_url": "https://sign.dendat.ai/verify?hash=3f9a2c...",
  "signers": [{ "name": "Jane Doe", "email": "[email protected]", "status": "signed", "signed_at": "..." }]
}

Respond 2xx to acknowledge. A failed delivery is retried once.

Verifying the webhook signature

Every webhook is signed so you can confirm it came from DENSIGN and was not tampered with in transit. The X-DenSign-Signature header is t=<timestamp>,v1=<signature>, where the signature is HMAC-SHA256 over "{timestamp}.{raw_body}" using your webhook signing secret.

Use the raw request body as bytes, not a re-serialized JSON object, or the signature will not match.

Python

import hmac, hashlib

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    expected = hmac.new(secret.encode(),
                        f'{parts["t"]}.{raw_body.decode()}'.encode(),
                        hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

Node.js

const crypto = require("crypto");
function verify(rawBody, header, secret) {
  const p = Object.fromEntries(header.split(",").map(s => s.split("=")));
  const expected = crypto.createHmac("sha256", secret)
    .update(`${p.t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(p.v1));
}

Public document verification

Anyone holding a signed DENSIGN PDF can confirm it is authentic and unaltered with no account, at https://sign.dendat.ai/verify, by entering the SHA-256 content hash printed on the certificate page. Lookup is by hash only, so the endpoint cannot be walked to discover documents.

Need an API key or higher limits? Upgrade to Business or email [email protected]
© 2026 DENDAT Technologies, Inc.