API access from Personal plan upward

SecuSign API Docs

Use the SecuSign API to create envelopes, upload PDFs, place signing fields, send documents for OTP-secured electronic signing, and retrieve the completed final PDF plus ledger evidence after signing.

Current API scope

The first public API version supports the core signing flow:

  • Create a draft envelope
  • Upload or replace a PDF
  • Set per-recipient field placements
  • Send the envelope for signing
  • List and read envelope status
  • Retrieve completed final PDFs, ledger PDFs, and ledger JSON
Free accounts cannot generate production API keys. Personal and Business accounts can use the core API under their normal envelope limits.

Authentication

Create an API key in Account → API Keys. The full key is shown once. Store it securely.

Authorization: Bearer ss_test_xxxxxxxxxxxxxxxxx

SecuSign stores only a protected hash of the API key. Revoked keys stop working immediately.

Base URL

Use the production API endpoint for live envelopes:

https://secusign.app

All API requests use HTTPS and Bearer-token authentication.

Connector demo download

SecuSign includes a customer-hostable connector demo for workflow automation. The connector receives signed SecuSign webhooks, verifies the HMAC signature, waits for envelope.completed, then retrieves the final PDF, ledger PDF, ledger JSON, envelope metadata, and verification URL through the API.

Use it as a reference foundation for OnlyOffice, Asana, Monday.com, CRM systems, file servers, or internal document platforms. Business plan is required for webhook-driven automation. Personal plan users can still use the API to poll envelope status and retrieve completed artifacts.

The connector calls https://secusign.app by default. Your webhook URL, however, must be your own public HTTPS endpoint that forwards to the connector running on your server.

tar -xzf secusign-connector-demo.tar.gz
cd secusign_connector_demo
cp .env.example .env
python3 -m pip install -r requirements.txt
python3 -m uvicorn connector:app --host 127.0.0.1 --port 8011

Do not commit real API keys or webhook secrets. The connector package includes only .env.example; runtime output and local secrets are excluded.

Quick start: full envelope flow

1

Create draft envelope

curl -s -X POST "$BASE_URL/api/v1/envelopes" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "client_token": "my-system-unique-id-001",
  "title": "API Contract Test",
  "message": "Please review and sign.",
  "expires_at": "2026-05-30",
  "signature_expiry": "2026-06-30",
  "recipients": [
    {
      "name": "Signer Name",
      "email": "[email protected]",
      "role": "signer",
      "stage": 1,
      "stamp_mode": "none"
    }
  ]
}
JSON
2

Upload PDF

curl -s -X POST "$BASE_URL/api/v1/envelopes/$EID/document" \
  -H "Authorization: Bearer $API_KEY" \
  -F "[email protected];type=application/pdf"
3

Set field placements

Page numbers are zero-based. Coordinates are percentages of page width/height.

curl -s -X PUT "$BASE_URL/api/v1/envelopes/$EID/fields" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "fields_by_recipient": {
    "[email protected]": {
      "sig": {
        "0": [
          { "x_pct": 42, "y_pct": 66, "w_mm": 45 }
        ]
      },
      "name": {
        "0": [
          { "x_pct": 42, "y_pct": 74, "font_pt": 10 }
        ]
      },
      "date": {
        "0": [
          { "x_pct": 42, "y_pct": 78, "font_pt": 10 }
        ]
      }
    }
  }
}
JSON
4

Send envelope

curl -s -X POST "$BASE_URL/api/v1/envelopes/$EID/send" \
  -H "Authorization: Bearer $API_KEY"
5

Read envelope status

Poll the envelope or list envelopes until status is completed. The response includes an artifacts block with the verification URL, final PDF hash, ledger PDF hash, and download paths when available.

curl -s "$BASE_URL/api/v1/envelopes/$EID" \
  -H "Authorization: Bearer $API_KEY"

curl -s "$BASE_URL/api/v1/envelopes?status=completed&limit=25" \
  -H "Authorization: Bearer $API_KEY"
6

Retrieve final PDF and ledger evidence

After completion, API clients can fetch the same final executed PDF, ledger PDF, and machine-readable ledger evidence that normal UI-created envelopes receive. Signing still happens through the secure signer page with OTP and audit events.

# Machine-readable evidence
curl -s "$BASE_URL/api/v1/envelopes/$EID/ledger" \
  -H "Authorization: Bearer $API_KEY"

# Final executed PDF
curl -L "$BASE_URL/api/v1/envelopes/$EID/final.pdf" \
  -H "Authorization: Bearer $API_KEY" \
  -o final.pdf

# Ledger PDF
curl -L "$BASE_URL/api/v1/envelopes/$EID/ledger.pdf" \
  -H "Authorization: Bearer $API_KEY" \
  -o ledger.pdf

Endpoints

MethodPathPurpose
GET/api/v1/meVerify API key and account plan.
POST/api/v1/envelopesCreate a draft envelope.
GET/api/v1/envelopesList envelopes owned by the API user. Supports status, limit, and offset.
GET/api/v1/envelopes/{eid}Read envelope status, metadata, and artifact availability.
POST/api/v1/envelopes/{eid}/documentUpload or replace PDF while draft.
PUT/api/v1/envelopes/{eid}/fieldsSet signing field placements.
POST/api/v1/envelopes/{eid}/sendSend/start signing flow.
GET/api/v1/envelopes/{eid}/ledgerReturn machine-readable ledger evidence for a completed envelope.
GET/api/v1/envelopes/{eid}/final.pdfDownload the final executed PDF after completion.
GET/api/v1/envelopes/{eid}/ledger.pdfDownload the ledger PDF after completion.

Recipient stamp modes

ModeMeaning
noneNo company stamp required for this signer.
senderUse the sender/account company stamp.
upload_optionalSigner may upload their own company stamp.
upload_requiredSigner must upload their company stamp before signing.

Planned next

  • Templates API
  • Bulk send API
  • Embedded signing options
  • Integration connectors for document/workflow platforms
  • Durable webhook queue hardening for higher-volume production use

Webhooks

Webhooks allow Business accounts to receive signed event notifications when an envelope is sent, signed, refused, or completed. Delivery is best-effort and does not block signing. Webhook payloads do not include document contents; use the Bearer-token API artifact endpoints to fetch the final PDF and ledger after envelope.completed.

Store the webhook signing secret securely. It is shown once when the webhook endpoint is created. SecuSign never sends raw signing tokens, OTP codes, API keys, or document contents in webhook payloads.

Available events

EventWhen it fires
webhook.test Manual test delivery for a configured webhook endpoint.
envelope.sent An envelope is sent for signing for the first time.
recipient.signed A recipient successfully completes signing.
recipient.refused A recipient refuses to sign. The refusal reason is included in this event only.
envelope.completed All required signing is complete and the final executed PDF / ledger have been generated.

Webhook delivery headers

HeaderMeaning
X-SecuSign-Event The event type, for example recipient.signed.
X-SecuSign-Delivery Unique event delivery id. Use this for idempotency.
X-SecuSign-Timestamp Unix timestamp used in the signature base string.
X-SecuSign-Signature HMAC-SHA256 signature in the format v1=<hex>.
User-Agent SecuSign-Webhooks/1.0

Payload shape

{
  "id": "evt_...",
  "type": "recipient.signed",
  "created_at": "2026-05-17T05:38:51Z",
  "data": {
    "event": "recipient.signed",
    "envelope": {
      "id": "env_...",
      "title": "Service Agreement",
      "status": "completed",
      "revision": 0,
      "created_at": "...",
      "updated_at": "...",
      "first_sent_at": "...",
      "completed_at": "...",
      "voided_at": "",
      "document": {
        "filename": "agreement.pdf",
        "size": 123456,
        "pages": 4
      },
      "recipients": [
        {
          "name": "Jane Doe",
          "email": "[email protected]",
          "role": "signer",
          "stage": 1,
          "status": "signed",
          "signed_at": "...",
          "refused_at": "",
          "stamp_mode": "none"
        }
      ],
      "cc_count": 0
    },
    "recipient": {
      "name": "Jane Doe",
      "email": "[email protected]",
      "role": "signer",
      "stage": 1,
      "status": "signed",
      "signed_at": "...",
      "refused_at": "",
      "stamp_mode": "none"
    },
    "meta": {
      "recipient_index": 1
    }
  }
}

Verify the webhook signature

Verify the signature using the raw request body exactly as received. Do not parse and re-serialize the JSON before verification.

# Python / Flask-style example
import hmac
import hashlib
import time

def verify_secusign_webhook(raw_body: bytes, headers: dict, secret: str) -> bool:
    timestamp = headers.get("X-SecuSign-Timestamp", "")
    signature = headers.get("X-SecuSign-Signature", "")

    if not timestamp or not signature.startswith("v1="):
        return False

    # Optional replay protection: reject payloads older than 5 minutes.
    now = int(time.time())
    try:
        ts_int = int(timestamp)
    except ValueError:
        return False

    if abs(now - ts_int) > 300:
        return False

    signed_payload = timestamp.encode("utf-8") + b"." + raw_body
    expected = hmac.new(
        secret.encode("utf-8"),
        signed_payload,
        hashlib.sha256
    ).hexdigest()

    received = signature.split("=", 1)[1]
    return hmac.compare_digest(expected, received)

Receiver behavior

  • Return any 2xx status to mark delivery as successful.
  • Use X-SecuSign-Delivery or payload id to ignore duplicate deliveries.
  • Process events asynchronously when possible, then respond quickly.
  • Use HTTPS endpoints only. Local/private webhook targets are blocked in production.
  • For recipient.refused, handle the refusal reason as sensitive business context.

Configure webhook endpoints

Webhook configuration is available for Business accounts. The management endpoints are session-authenticated account routes, while signing automation uses the Bearer-token /api/v1/* API.

# List configured webhooks for the logged-in account
GET /api/me/webhooks

# Create a webhook endpoint
POST /api/me/webhooks
Content-Type: application/json

{
  "name": "Production webhook",
  "url": "https://example.com/secusign/webhook",
  "events": [
    "envelope.sent",
    "recipient.signed",
    "recipient.refused",
    "envelope.completed"
  ],
  "enabled": true
}

# Test delivery
POST /api/me/webhooks/{webhook_id}/test

# Delete / disable endpoint
DELETE /api/me/webhooks/{webhook_id}

Rate limits

API calls are rate-limited per API key to protect service stability. Envelope sends still use the normal plan envelope limits.

PlanAPI request limit
Personal60 requests per minute
Business300 requests per minute

Error model

Typical API errors return JSON with a detail message.

StatusMeaning
400Invalid payload, missing PDF, invalid email, invalid field position.
401Missing or invalid Bearer API key.
402Plan does not include API access.
403API key lacks required scope.
409Envelope is locked because it is no longer a draft.
413Uploaded PDF is too large or has too many pages.
429API rate limit exceeded. Retry after the period shown in the Retry-After response header.