API Reference
·https://api.docura.cloud/v1Getting started
Docura API Reference
The Docura REST API lets you create templates, send documents for signing, track status, download signed PDFs, and receive real-time webhook notifications — all programmatically.
The API follows standard REST conventions: JSON request and response bodies, HTTP verbs for actions, and predictable resource URLs. All endpoints return consistent error shapes.
Authentication
All API requests must include a secret API key in the Authorization header. Keys are prefixed dsign_ and scoped to your organisation.
curl https://api.docura.cloud/v1/templates \ -H "Authorization: Bearer dsign_live_your_api_key"
Keys are created in the dashboard. You can have up to 10 active keys per organisation. Each key can be named and revoked independently.
Quickstart
Send your first document for signing in 3 API calls. First, create a template in the dashboard (drag your PDF fields visually), then use the API to send it.
Step 1 — Get your template ID
curl https://api.docura.cloud/v1/templates \ -H "Authorization: Bearer dsign_live_..." # Response → pick a template id from "data"
Step 2 — Create a submission (send for signing)
curl -X POST https://api.docura.cloud/v1/templates/tmpl_01HZ.../submissions \
-H "Authorization: Bearer dsign_live_..." \
-H "Content-Type: application/json" \
-d '{
"signers": [
{
"roleId": "role_01HZ...",
"email": "jane@acme.com",
"name": "Jane Smith"
}
],
"signingOrder": "PARALLEL",
"message": "Please review and sign the attached NDA.",
"expiresInDays": 7
}'Step 3 — Poll for completion or use a webhook
curl https://api.docura.cloud/v1/submissions/sub_01HZ... \ -H "Authorization: Bearer dsign_live_..." # When status === "COMPLETED" → download the signed PDF curl https://api.docura.cloud/v1/submissions/sub_01HZ.../download \ -H "Authorization: Bearer dsign_live_..." \ --output signed-document.pdf
Base URL & versioning
Base URL: https://api.docura.cloud/v1 Version: v1 (current, stable) Format: JSON — Content-Type: application/json
The API is versioned by path prefix. Breaking changes will be introduced in a new version (/v2) with a 6-month deprecation window for v1.
Templates
A template is a PDF document with pre-defined field positions and signer roles. Templates are created visually in the dashboard builder and then sent to signers via the submissions endpoint.
GET List templates
Returns a paginated list of active templates for your organisation.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number. Defaults to 1. |
limit | integer | No | Results per page. 1–100. Defaults to 20. |
Response
{
"data": [
{
"id": "tmpl_01HZ5X9BKPQR3V2M",
"name": "Non-Disclosure Agreement",
"description": "Standard 2-page NDA for contractors",
"pageCount": 2,
"signerRoles": [
{ "id": "role_01HZ...", "name": "Client", "order": 1, "color": "#6366f1" },
{ "id": "role_02HZ...", "name": "Vendor", "order": 2, "color": "#f59e0b" }
],
"createdAt": "2026-01-15T09:00:00Z",
"updatedAt": "2026-03-20T14:30:00Z"
}
],
"total": 12,
"page": 1,
"limit": 20
}GET Get a template
{
"id": "tmpl_01HZ5X9BKPQR3V2M",
"name": "Non-Disclosure Agreement",
"description": "Standard 2-page NDA for contractors",
"pageCount": 2,
"signerRoles": [
{ "id": "role_01HZ...", "name": "Client", "order": 1, "color": "#6366f1" }
],
"createdAt": "2026-01-15T09:00:00Z",
"updatedAt": "2026-03-20T14:30:00Z"
}PUT Update a template
Update a template's name or description. All fields are optional.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | No | Template name. 1–200 characters. |
description | string | No | Optional description. Max 1,000 characters. |
curl -X PUT https://api.docura.cloud/v1/templates/tmpl_01HZ... \
-H "Authorization: Bearer dsign_live_..." \
-H "Content-Type: application/json" \
-d '{ "name": "NDA v2 — Updated" }'DELETE Delete a template
Soft-deletes a template. It will no longer appear in lists or accept new submissions.
{ "success": true }Submissions
A submission is an instance of a template sent to one or more signers. Creating a submission immediately emails all signers (or the first signer if sequential) with a unique signing link.
POST Create a submission
Sends the template to all specified signers. Each signer must correspond to a signer role defined on the template. Use GET /templates/:id to retrieve role IDs.
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
signers | array | Yes | Array of signer objects. At least one required. |
signers[].roleId | string | Yes | The signer role ID from the template. |
signers[].email | string | Yes | Signer's email address. Invitation sent here. |
signers[].name | string | No | Signer's display name. |
signingOrder | enum | No | "PARALLEL" (default) or "SEQUENTIAL". Sequential sends one at a time in role order. |
message | string | No | Custom message included in the signing invitation email. Max 1,000 characters. |
expiresInDays | integer | No | Days until the signing link expires. 1–365. |
curl -X POST https://api.docura.cloud/v1/templates/tmpl_01HZ.../submissions \
-H "Authorization: Bearer dsign_live_..." \
-H "Content-Type: application/json" \
-d '{
"signers": [
{
"roleId": "role_01HZ5X9BKPQR3V2M",
"email": "jane@acme.com",
"name": "Jane Smith"
}
],
"signingOrder": "PARALLEL",
"message": "Hi Jane, please sign the attached NDA before our kickoff call.",
"expiresInDays": 7
}'Response — 201 Created
{
"id": "sub_01HZ8P4NKQR2V3M",
"status": "PENDING",
"signingOrder": "PARALLEL",
"signers": [
{
"id": "sgn_01HZ...",
"email": "jane@acme.com",
"name": "Jane Smith",
"roleName": "Client",
"status": "PENDING",
"signingUrl": "https://app.docura.cloud/sign/tok_eyJh..."
}
],
"createdAt": "2026-06-24T10:30:00Z"
}signingUrl if you want to embed signing in your own UI. URLs expire when the submission does.GET Get a submission
Returns full submission details including per-signer status. Poll this to track progress.
{
"id": "sub_01HZ8P4NKQR2V3M",
"status": "COMPLETED",
"signingOrder": "PARALLEL",
"template": { "id": "tmpl_01HZ...", "name": "Non-Disclosure Agreement" },
"message": "Hi Jane, please sign the attached NDA...",
"expiresAt": "2026-07-01T10:30:00Z",
"completedAt": "2026-06-24T11:15:00Z",
"createdAt": "2026-06-24T10:30:00Z",
"signers": [
{
"id": "sgn_01HZ...",
"email": "jane@acme.com",
"name": "Jane Smith",
"roleName": "Client",
"status": "SIGNED",
"signedAt": "2026-06-24T11:15:00Z",
"order": 1
}
]
}GET Download signed PDF
Returns the finalised, signed PDF as a binary stream. The submission must have status === "COMPLETED".
curl https://api.docura.cloud/v1/submissions/sub_01HZ.../download \ -H "Authorization: Bearer dsign_live_..." \ --output signed-nda-jane-smith.pdf
Content-Type: application/pdf. A download audit event is recorded on each call.GET Audit trail
Returns the full chronological event log for the submission. Use this to prove signing sequence, timestamps, and IP addresses in legal proceedings.
{
"submissionId": "sub_01HZ8P4NKQR2V3M",
"events": [
{
"id": "evt_01HZ...",
"eventType": "SUBMISSION_CREATED",
"signerId": null,
"ipAddress": "203.0.113.1",
"createdAt": "2026-06-24T10:30:00Z",
"metadata": {}
},
{
"id": "evt_02HZ...",
"eventType": "SIGNER_VIEWED",
"signerId": "sgn_01HZ...",
"ipAddress": "198.51.100.42",
"createdAt": "2026-06-24T11:10:00Z",
"metadata": { "userAgent": "Mozilla/5.0..." }
},
{
"id": "evt_03HZ...",
"eventType": "SIGNER_SIGNED",
"signerId": "sgn_01HZ...",
"ipAddress": "198.51.100.42",
"createdAt": "2026-06-24T11:15:00Z",
"metadata": {}
},
{
"id": "evt_04HZ...",
"eventType": "SUBMISSION_COMPLETED",
"signerId": null,
"ipAddress": null,
"createdAt": "2026-06-24T11:15:01Z",
"metadata": {}
}
]
}DELETE Void a submission
Voids an in-progress submission. Signing links are immediately invalidated. Cannot void a submission with status === "COMPLETED" or "VOIDED".
{ "success": true }Webhooks
Webhooks deliver real-time HTTP POST notifications to your server when submission or signer events occur. This is more reliable than polling — configure a webhook once and receive events as they happen.
POST Create a webhook
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS URL that will receive POST requests. Must be publicly reachable. |
events | string[] | Yes | Array of event types to subscribe to. At least one required. |
secret | string | No | Signing secret for payload verification. Min 8 characters. Auto-generated if omitted. |
curl -X POST https://api.docura.cloud/v1/webhooks \
-H "Authorization: Bearer dsign_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/docura",
"events": [
"submission.completed",
"signer.signed",
"submission.expired"
],
"secret": "wh_secret_super_secure_value"
}'{
"id": "wh_01HZ...",
"url": "https://your-app.com/webhooks/docura",
"events": ["submission.completed", "signer.signed", "submission.expired"],
"secret": "wh_secret_super_secure_value",
"isActive": true
}GET List webhooks
{
"data": [
{
"id": "wh_01HZ...",
"url": "https://your-app.com/webhooks/docura",
"events": ["submission.completed", "signer.signed"],
"isActive": true,
"createdAt": "2026-06-01T09:00:00Z"
}
]
}PUT Update a webhook
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | No | New destination URL. |
events | string[] | No | Replace the subscribed event types. Min 1. |
isActive | boolean | No | Enable or disable the webhook without deleting it. |
DELETE Delete a webhook
{ "success": true }Event types
Subscribe to any combination of the following events:
| Event | When it fires |
|---|---|
submission.created | A new submission is created |
submission.completed | All signers have signed |
submission.expired | The signing deadline passed |
submission.declined | Any signer declined |
submission.voided | The submission was voided via API or dashboard |
signer.viewed | A signer opened their signing link |
signer.signed | A signer completed signing |
signer.declined | A signer declined to sign |
Verifying payloads
Every webhook request includes a X-Docura-Signature header — an HMAC-SHA256 of the raw request body signed with your webhook secret. Verify it to ensure requests are genuinely from Docura.
import crypto from "crypto";
export function verifyWebhook(
body: string, // raw request body as a string
signature: string, // X-Docura-Signature header value
secret: string // your webhook secret
): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(body, "utf8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(signature, "hex")
);
}// Example webhook payload
{
"event": "submission.completed",
"timestamp": "2026-06-24T11:15:01Z",
"data": {
"submissionId": "sub_01HZ8P4NKQR2V3M",
"status": "COMPLETED",
"templateId": "tmpl_01HZ...",
"completedAt": "2026-06-24T11:15:00Z"
}
}Reference
Status enums
Submission status
| Parameter | Type | Required | Description |
|---|---|---|---|
DRAFT | string | No | Created but not yet sent. |
PENDING | string | No | Sent. Waiting for all signers. |
COMPLETED | string | No | All signers have signed. PDF available to download. |
DECLINED | string | No | One or more signers declined. |
EXPIRED | string | No | Signing deadline passed before completion. |
VOIDED | string | No | Cancelled via API or dashboard. |
Signer status
| Parameter | Type | Required | Description |
|---|---|---|---|
PENDING | string | No | Invitation not yet sent (sequential, waiting for prior signer). |
INVITED | string | No | Invitation email sent. |
VIEWED | string | No | Signer opened the signing link. |
SIGNED | string | No | Signer completed all fields. |
DECLINED | string | No | Signer declined to sign. |
Error codes
All errors return a consistent JSON shape:
{
"error": {
"code": "SUBMISSION_ALREADY_COMPLETED",
"message": "Cannot void a completed submission.",
"status": 422
}
}| HTTP | Cause |
|---|---|
400 | Invalid request body — Zod validation failed. Check the error details. |
401 | Missing or invalid API key. |
403 | The resource exists but belongs to a different organisation. |
404 | Resource not found. |
422 | Business logic error — e.g. voiding a completed submission. |
429 | Rate limit exceeded. See X-RateLimit-Reset header. |
500 | Unexpected server error. Retrying with exponential backoff is safe. |
Rate limits
Requests are rate-limited per API key. Response headers indicate your current usage:
X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 997 X-RateLimit-Reset: 1719230400 # Unix timestamp
| Plan | Requests / minute | Requests / day |
|---|---|---|
| Free | 60 | 500 |
| Starter | 300 | 5,000 |
| Professional | 600 | 20,000 |
| Enterprise | Custom | Custom |
X-RateLimit-Reset before retrying. Use exponential backoff for reliability.