API reference

The QuillSign REST API lets you create, send, and track envelopes programmatically. The base URL is https://quillsign-dev.com/v1. A machine-readable description is published at /v1/openapi.json.

Authentication

Create an API key under Profile → Developer → API keys. The secret is shown once. Send it as a bearer token:

Authorization: Bearer qsk_xxxxxxxxxxxxxxxxxxxxxxxx

Keys inherit your account's organization and role. Revoke a key any time from the same screen.

Conventions

  • Idempotency. Send an Idempotency-Key header on create/send; a retry with the same key returns the original response (and an Idempotent-Replay: true header).
  • Pagination. List endpoints accept ?limit= and ?cursor= and return { "envelopes": [...], "cursor": "..." }; pass the cursor back to fetch the next page.
  • Content type. Request and response bodies are JSON. PDFs are base64-encoded on upload and returned as binary on download.

POST/v1/envelopes

Create a draft envelope from a base64-encoded PDF.

curl https://quillsign-dev.com/v1/envelopes \
  -H "Authorization: Bearer qsk_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-4821" \
  -d '{
    "title": "Master Services Agreement",
    "filename": "msa.pdf",
    "fileBase64": "JVBERi0xLjcKJ…"
  }'

Returns 201 with the envelope, including its id.

PUT/v1/envelopes/{id}

Set recipients and fields on a draft. Fields use normalized coordinates (0–1) and reference a recipient and a document page.

curl -X PUT https://quillsign-dev.com/v1/envelopes/$ID \
  -H "Authorization: Bearer qsk_…" -H "Content-Type: application/json" \
  -d '{
    "routing": "sequential",
    "recipients": [
      {"id":"r1","name":"Dana Lee","email":"dana@acme.com","role":"signer","order":1}
    ],
    "fields": [
      {"id":"f1","recipientId":"r1","type":"signature","docIndex":0,
       "page":0,"x":0.1,"y":0.8,"w":0.25,"h":0.05}
    ]
  }'

POST/v1/envelopes/{id}/send

Send the envelope for signature. Each recipient receives a signing token; the response includes per-recipient signUrl values you can distribute.

GET/v1/envelopes/{id}

Fetch an envelope's current status, recipients, fields, audit trail, and (once complete) completedHash.

GET/v1/envelopes/{id}/document

Download the working PDF, or the sealed copy with ?completed=true. Returns application/pdf.

GET/v1/envelopes

List your envelopes, newest first. Supports limit and cursor.

GET/v1/templates  ·  POST/v1/templates/{id}/use

List available templates, then create (and optionally send) an envelope from one by supplying recipients for its roles.

curl https://quillsign-dev.com/v1/templates/$TPL/use \
  -H "Authorization: Bearer qsk_…" -H "Content-Type: application/json" \
  -d '{"sendNow": true,
       "recipients":[{"roleId":"r1","name":"Dana Lee","email":"dana@acme.com"}]}'

Webhooks

Register an endpoint under Profile → Developer → Webhooks. QuillSign POSTs a JSON body on these events:

envelope.sent, envelope.viewed, envelope.recipient_signed, envelope.recipient_approved, envelope.completed, envelope.declined, envelope.voided, envelope.expired.

Each delivery is signed. Verify it with the signing secret shown when you created the webhook:

// headers: X-QuillSign-Timestamp, X-QuillSign-Signature: t=<ts>,v1=<hmac>
const expected = crypto
  .createHmac("sha256", webhookSecret)
  .update(`${timestamp}.${rawBody}`)
  .digest("hex");
// constant-time compare expected against the v1 value

Delivery is durable: failed attempts are retried and dead-lettered, so a brief outage on your side won't drop events.

Errors

Errors return a JSON body { "error": "message" } with an appropriate HTTP status: 400 validation, 401 bad/missing key, 403 not permitted, 404 not found, 409 conflict, 429 rate-limited.