Start signing rounds from your own systems

The DOXIO REST API runs your templates with data, tells you what happens through signed webhooks, and hands you the signed PDF back. Read access is available from Tým; starting runs and webhooks from Kancelář.

Quick start

  1. 1. Create a template and a key

    Save a document as a template in the app, then create an API key in Settings → API. The key is shown once.

  2. 2. Start a run

    POST the recipients and prefill values to the template. The organization signs automatically when the template says so; everyone else gets an e-mail.

  3. 3. Listen and download

    Register a webhook for run.completed and download the signed PDF from the run.

Authentication

Send the key as a bearer token on every request. Keys belong to the organization; revoke them any time in Settings → API. Never put a key in browser code.

curl -X POST https://doxio.pro/api/v1/templates/TEMPLATE_ID/runs \
  -H "Authorization: Bearer $DOXIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-2026-0001" \
  -d '{
    "recipients": { "client": { "email": "jana@example.com", "name": "Jana Nováková", "locale": "cs" } },
    "prefill": { "contract_no": "2026-0001" },
    "external_id": "ORDER-2026-0001"
  }'

Endpoints

https://doxio.pro/api/v1

  • GET /templates

    List your active templates with their slots and fields

  • GET /templates/{id}

    One template

  • POST /templates/{id}/runs

    Start a signing round from a template

  • GET /runs

    List runs (50 per page, filter by external_id and status)

  • GET /runs/{id}

    One run with every signer's status

  • POST /runs/{id}/cancel

    Cancel a run: every open signing link stops working

  • GET /runs/{id}/signed.pdf

    Download the signed PDF of a completed run

  • GET /runs/{id}/certificate.pdf

    Download the separate certificate (separate certificate mode only)

curl https://doxio.pro/api/v1/runs/RUN_ID \
  -H "Authorization: Bearer $DOXIO_API_KEY"

curl -o signed.pdf https://doxio.pro/api/v1/runs/RUN_ID/signed.pdf \
  -H "Authorization: Bearer $DOXIO_API_KEY"

Errors

Errors are JSON: an error code, plus issues for invalid input or field_errors when the data does not fit the template. Every response is marked no-store.

  • 400 invalid_input
  • 400 invalid_json
  • 400 invalid_idempotency_key
  • 400 invalid_cursor
  • 401 unauthorized
  • 402 plan_required
  • 402 billing_inactive
  • 403 forbidden
  • 404 not_found
  • 409 org_signature_not_configured
  • 409 mandate_required
  • 409 quota_exceeded
  • 409 template_unavailable
  • 409 idempotency_in_progress
  • 409 idempotency_conflict
  • 409 run_completed
  • 409 run_completing
  • 409 run_not_cancellable
  • 413 payload_too_large
  • 415 unsupported_media_type
  • 422 invalid_fields
  • 429 rate_limited
  • 500 internal_error

Idempotency

Send an Idempotency-Key header when starting a run. Repeating the same request with the same key within 24 hours returns the original run (200) instead of creating a second one; the same key with a different body is refused with 409 idempotency_conflict.

Webhooks

Add an https endpoint in Settings → API. DOXIO POSTs a JSON event with the current run; failed deliveries are retried after 1 min, 5 min, 30 min, 2 h, 12 h and 24 h. An endpoint that fails 50 times in a row is disabled and the owner is e-mailed.

Events

run.signer_signed
A signer signed (including the automatic organization signature).
run.completed
Every signer signed; the signed PDF is ready.
run.declined
A signer declined; the run is over.
run.cancelled
The run was cancelled through the API.
run.expired
A signer's link lapsed, so the round cannot finish on its own.
run.org_signature_failed
The automatic organization signature could not be applied; DOXIO retries daily.
POST /your/webhook HTTP/1.1
Content-Type: application/json
User-Agent: doxio-webhooks/1
X-Doxio-Event: run.completed
X-Doxio-Delivery: 7b0c…            (unique per delivery — deduplicate on it)
X-Doxio-Timestamp: 1790000000      (unix seconds)
X-Doxio-Signature: v1=<hex HMAC-SHA256(secret, timestamp + "." + body)>

Verifying a webhook

Compute HMAC-SHA256 over the timestamp, a dot and the raw request body with your endpoint secret, compare it in constant time with the v1 value of X-Doxio-Signature, and reject timestamps older than 5 minutes. Deduplicate on X-Doxio-Delivery.

const { createHmac, timingSafeEqual } = require("node:crypto");

// rawBody: the request body exactly as received (a string, BEFORE JSON.parse).
function verifyDoxioWebhook(rawBody, headers, secret, nowMs = Date.now()) {
  const timestamp = String(headers["x-doxio-timestamp"] ?? "");
  if (!/^\d+$/.test(timestamp) || Math.abs(nowMs / 1000 - Number(timestamp)) > 300) return false;
  const expected = createHmac("sha256", secret).update(timestamp + "." + rawBody).digest();
  return String(headers["x-doxio-signature"] ?? "")
    .split(",")
    .some((part) => {
      const match = /^\s*v1=([0-9a-f]{64})\s*$/.exec(part);
      return match !== null && timingSafeEqual(Buffer.from(match[1], "hex"), expected);
    });
}

Worked example

secret    = whk_test_secret_0123456789abcdefghijklmnopqrstu
timestamp = 1790000000
body      = {"id":"evt_1","type":"run.completed","created_at":"2026-09-28T12:00:00.000Z","data":{"run":{"id":"r1"}}}
signature = v1=54849a4be185576b3f3670eb41e9005d41f751baa60c65028ebf4e4af63a5a01

Limits

  • 120 requests per minute per key and 600 per minute per organization (429 with Retry-After).
  • Request bodies up to 256 KB, JSON only.
  • Up to 10 active keys and 5 webhook endpoints per organization.
  • Webhook deliveries are kept for 30 days. Webhooks never carry files.