Khatm
Khatm Developer Sandbox API
Integrate the Khatm signing workflow into your application.
Create requests, detect and confirm fields, remind signers, cancel workflows, and retrieve completed documents with their evidence.
Quickstart
- Create a Sandbox key from the API Sandbox tab in your workspace. Copy the khatm_test_… key and the whsec_… webhook secret: both are shown only once.
- Create a request with the PDF and your signers:
curl "$KHATM_BASE_URL/v1/signature-requests" \
-H "Authorization: Bearer $KHATM_API_KEY" \
-F "document=@contract.pdf;type=application/pdf" \
-F 'request={
"client_reference": "contract-123",
"signers": [{ "reference": "customer", "name": "Alice Martin",
"email": "alice@example.com", "order": 1 }],
"fields": [{ "signer_reference": "customer", "type": "signature",
"page": 1, "x": 90, "y": 610, "width": 170, "height": 55 }]
}'- Activate it with POST /signature-requests/{id}/activate to get one signing_url per signer.
- Track the status by webhook or with GET /signature-requests/{id}, then download the signed PDF and evidence from /artifacts/{type}.
No coordinates at hand? Omit fields, call POST /signature-requests/{id}/detect-fields, then confirm the suggestions with PUT /signature-requests/{id}/fields.
Back to your app
Add return_url when creating the request: after signing (or declining), the signer is sent back to your app automatically. Khatm appends khatm_status (signed, declined, cancelled or expired), signature_request_id, client_reference and signer_reference. Use them for the UI, then confirm the outcome with the API or a webhook.
Other useful options:
- locale (fr, en, ar) per signer sets the signing page language.
- sequential_signing: true enforces the signers' order.
- GET /signature-requests lists your requests (status and client_reference filters, cursor pagination).
- POST /templates creates a reusable template through the API.
Changing a signer
Wrong address, or someone left? Call POST /v1/signature-requests/{id}/signers/{reference}/replace with email (plus name, locale, send_email). Before activation, the signer is simply corrected. After activation, the previous link is revoked and the new signer keeps the same reference, order and fields; the response returns their new signing_url. A signer who already signed or declined can no longer be replaced (409).
Customizing emails
Add an email object at creation: sender_name (your company), subject, message (plain text, line breaks kept) and logo_url (HTTPS image). Khatm uses it for the invitation, reminders and signer replacement. Each email is written in the signer's locale (fr, en or ar, right-to-left for Arabic) and states that it is sent on your behalf.
Bulk send
POST /v1/bulk-sends sends one template to up to 50 recipients in one call: each recipient (client_reference, signers, field_values) becomes a request that is created and activated. Results are reported per row (active, already_exists or error); an invalid row does not stop the others. Replaying the same batch creates no duplicates and sends no extra emails.
Verifying the signer's identity
Add "verification": "email_otp" to a signer. Before seeing the document, they receive a 6-digit code by email (valid 10 minutes, 5 attempts). The document, signing and declining stay locked on the server until the code is entered. The check shows in the status (verified_at) and in the evidence.
Embedding signing in your app
Set embed_origin (for example https://app.example.com) at creation. Activation then returns an embed_url per signer, to load in an <iframe> on that origin; no other site can display it. The page sends postMessage events to your window (signing.loaded, signing.verification_required, signing.signed, signing.declined, and signing.closed with a status when the link can no longer be used) with signatureRequestId, clientReference and signerReference. Check event.origin and confirm the outcome with a webhook or the status endpoint.
SDKs
Dependency-free Node.js, Python and PHP clients are available in the repository (sdks/). They add an Idempotency-Key automatically, retry 429 and 503 responses, and verify webhooks.
Authentication
Every request sends the key in the Authorization: Bearer khatm_test_… header. Treat it like a password: keep it server-side, never in the browser, a URL or logs. A revoked key is rejected immediately.
Verifying webhooks
Khatm signs webhook-id + "." + webhook-timestamp + "." + raw body with HMAC-SHA256 and your whsec_… secret. The webhook-signature header is v1, followed by the base64 signature.
- Verify the raw bytes with a constant-time comparison.
- Reject stale timestamps.
- Deduplicate with webhook-id: delivery is at-least-once, with retries (immediately, then about 1 min, 5 min, 30 min and 2 h).
Events: signature_request.activated, signer.viewed (first opening of the link), signer.completed, signer.declined, signer.replaced, signer.expired, then signature_request.completed, .declined, .cancelled or .expired. Signer events include data.signer (reference, email, status). Arrival order is not guaranteed: when in doubt, read GET /v1/signature-requests/{id}.
Tracking and replay: GET /v1/webhook-deliveries lists every event with its state (pending, delivered, failed) and last error. Once your receiver is fixed, POST /v1/webhook-deliveries/{id}/replay sends the event again right away, with the same webhook-id.
Limits and retention
- PDFs up to 8 MiB, up to 10 signers and 100 fields per request.
- Rate limit exceeded: 429 response, with X-RateLimit-Limit and X-RateLimit-Remaining headers.
- Temporary outage: 503 temporarily_unavailable response. Retry with increasing delays; replaying a create call with the same client_reference never creates a duplicate.
- Content is deleted 10 days after upload: download the signed PDF and evidence before retention_expires_at.
API v1
POST /v1/signature-requestsBring the PDF, signers and fields together in one request. A client reference connects it to your business flow.POST /v1/signature-requests/{id}/activateActivate the request to obtain signing links. Place each URL in the right user journey.URL Khatm signing experienceThe signer opens their link, reviews the document and completes the fields assigned to them.EVENT signature_request.completedYour application receives the completion event. Verify it before updating your workflow.GET /v1/signature-requests/{id}/artifacts/{type}Retrieve the finalized document and its evidence, then attach them to the customer record.