API v1 — Stable

Developer API

Send documents for e-signature, embed signing UIs, and automate workflows. The easiest way to add legally binding signatures to your app.

Getting Started

1

Create an account

Sign up free at signforge.io/register

2

Generate an API key

Go to Dashboard → Developers and create a live or test key.

3

Make your first request

Send a document for signing with a single API call:

bash
curl -X POST https://signforge.io/api/v1/quick-sign \
  -H "X-API-Key: sf_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "NDA Agreement",
    "pdf_base64": "<base64-encoded-pdf>",
    "signer_email": "jane@example.com",
    "signer_name": "Jane Doe"
  }'

Authentication

All V1 API endpoints authenticate via the X-API-Key header.

Live keys

Prefix: sf_live_

Create real envelopes, send emails, consume quota.

Test keys

Prefix: sf_test_

Sandbox mode — watermarked PDFs, no emails sent, no quota consumed.

Sandbox Mode

Test keys (prefixed sf_test_) enable sandbox mode for safe development and testing.

Watermarked PDFs

Signed PDFs include a visible "SANDBOX" watermark so they can't be mistaken for production documents.

No emails sent

Signing invitations are suppressed. Use the signing_url from the API response to test the signing flow.

No quota consumed

Sandbox operations don't count against your monthly envelope quota.

Endpoints Reference

Envelopes

POST/api/v1/envelopesCreate envelope
POST/api/v1/envelopes/{id}/sendSend for signing
GET/api/v1/envelopes/{id}Get status
GET/api/v1/envelopesList envelopes
GET/api/v1/envelopes/{id}/documents/{kind}Download PDF
GET/api/v1/envelopes/{id}/archiveDownload ZIP archive
POST/api/v1/envelopes/{id}/voidVoid envelope
DELETE/api/v1/envelopes/{id}Delete
POST/api/v1/quick-signOne-call sign
POST/api/v1/envelopes/{id}/embed-urlGet embed URL
POST/api/v1/envelopes/{id}/anchor-fieldsPlace fields by text anchor

Templates

GET/api/v1/templatesList templates
GET/api/v1/templates/{id}Get with fields
POST/api/v1/templates/{id}/useCreate envelope from template

Webhooks

POST/api/v1/webhooksRegister
GET/api/v1/webhooksList
PATCH/api/v1/webhooks/{id}Update
DELETE/api/v1/webhooks/{id}Delete
GET/api/v1/webhooks/{id}/deliveriesDelivery log

Templates

Create reusable templates in the dashboard, then generate pre-filled envelopes via the API. Templates preserve field placements so you don't need to specify coordinates each time.

python
import requests

API_KEY = "sf_live_YOUR_KEY"
BASE = "https://signforge.io/api/v1"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}

# List available templates
templates = requests.get(f"{BASE}/templates", headers=HEADERS).json()
template = templates["items"][0]
print(f"Using template: {template['name']}")

# Create an envelope from template with prefilled fields
resp = requests.post(
    f"{BASE}/templates/{template['id']}/use",
    headers=HEADERS,
    json={
        "recipients": [
            {"email": "client@company.com", "name": "Jane Doe"}
        ],
        "title": "NDA — Jane Doe",
        "prefill_fields": {
            "date": "2026-04-14",
            "company_name": "Acme Corp"
        },
        "send_immediately": True,
    },
)
data = resp.json()
print(f"Envelope: {data['id']}, Status: {data['status']}")

Anchor-Based Field Placement

Instead of specifying exact coordinates, find text in the PDF and place fields relative to it. The API searches for the anchor text and positions the field automatically.

python
# Place fields by finding text anchors in the PDF
resp = requests.post(
    f"{BASE}/envelopes/{envelope_id}/anchor-fields",
    headers=HEADERS,
    json={
        "anchors": [
            {
                "anchor_text": "Signature:",
                "field_type": "signature",
                "recipient_index": 0,
                "offset_x": 0.15,
                "offset_y": 0.01,
                "width": 0.25,
                "height": 0.04,
            },
            {
                "anchor_text": "Date:",
                "field_type": "date",
                "recipient_index": 0,
                "offset_x": 0.08,
                "offset_y": 0.01,
            },
        ]
    },
)
result = resp.json()
print(f"Created {result['fields_created']} fields, {result['fields_not_found']} not found")

Tip: Anchor placement works best with unique text labels like “Signature:” or “Client Name:”. If the text appears multiple times, specify page_index to narrow the search.

Quick Sign

The fastest way to get a document signed. One call creates the envelope, uploads the PDF, adds the signer, and sends the signing email.

python
import requests, base64

with open("contract.pdf", "rb") as f:
    pdf_b64 = base64.b64encode(f.read()).decode()

response = requests.post(
    "https://signforge.io/api/v1/quick-sign",
    headers={"X-API-Key": "sf_live_YOUR_KEY"},
    json={
        "title": "Service Agreement",
        "pdf_base64": pdf_b64,
        "signer_email": "client@company.com",
        "signer_name": "Alex Chen",
    },
)

data = response.json()
print(f"Envelope: {data['envelope_id']}")
print(f"Status: {data['status']}")

Webhooks

Get real-time notifications when envelopes change state. All webhook payloads are signed with HMAC-SHA256.

Events

envelope.created

Envelope was created

envelope.sent

Envelope sent for signing

envelope.completed

All recipients signed

envelope.voided

Sender voided the envelope

envelope.expired

Envelope reached expiry

recipient.viewed

Recipient opened document

recipient.signed

Recipient completed signing

recipient.declined

Recipient declined to sign

Payload Format

json
{
  "event": "envelope.completed",
  "envelope_id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2026-03-25T12:00:00Z",
  "data": {
    "title": "Service Agreement",
    "status": "completed",
    "recipients": [
      {
        "email": "signer@example.com",
        "name": "Jane Doe",
        "status": "signed",
        "signed_at": "2026-03-25T11:59:00Z"
      }
    ],
    "download_urls": {
      "signed": "/api/v1/envelopes/{id}/documents/signed",
      "certificate": "/api/v1/envelopes/{id}/documents/certificate"
    }
  }
}

HMAC Verification

javascript
const crypto = require("crypto");

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// In your Express handler:
app.post("/webhook", (req, res) => {
  const sig = req.headers["x-webhook-signature"];
  if (!verifyWebhook(req.rawBody, sig, WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }
  const event = req.body;
  console.log("Event:", event.event, event.envelope_id);
  res.sendStatus(200);
});

Rate Limits & Quotas

PlanAPI Envelopes / MonthRate Limit
Free560 req/min
Pro10060 req/min
Business50060 req/min
EnterpriseUnlimitedCustom

Test/sandbox keys are exempt from monthly quotas but still subject to rate limits.

Error Codes

CodeMeaning
400Invalid request — check the request body
401Missing or invalid API key
403Account suspended or feature not available
404Resource not found
422Validation error — details in response
429Rate limit or quota exceeded
500Internal server error

Verification API

Verify signed documents — public, no auth required.

Embed Widget

Embed signing UIs in your app with an iframe and JS SDK.

MCP Server

Use SignForge from Claude Desktop and AI agents.

Code Examples

Integration samples for n8n, Retool, cURL, and more.