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
Create an account
Sign up free at signforge.io/register
Generate an API key
Go to Dashboard → Developers and create a live or test key.
Make your first request
Send a document for signing with a single API call:
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
/api/v1/envelopesCreate envelope/api/v1/envelopes/{id}/sendSend for signing/api/v1/envelopes/{id}Get status/api/v1/envelopesList envelopes/api/v1/envelopes/{id}/documents/{kind}Download PDF/api/v1/envelopes/{id}/archiveDownload ZIP archive/api/v1/envelopes/{id}/voidVoid envelope/api/v1/envelopes/{id}Delete/api/v1/quick-signOne-call sign/api/v1/envelopes/{id}/embed-urlGet embed URL/api/v1/envelopes/{id}/anchor-fieldsPlace fields by text anchorTemplates
/api/v1/templatesList templates/api/v1/templates/{id}Get with fields/api/v1/templates/{id}/useCreate envelope from templateWebhooks
/api/v1/webhooksRegister/api/v1/webhooksList/api/v1/webhooks/{id}Update/api/v1/webhooks/{id}Delete/api/v1/webhooks/{id}/deliveriesDelivery logTemplates
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.
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.
# 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.
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.createdEnvelope was created
envelope.sentEnvelope sent for signing
envelope.completedAll recipients signed
envelope.voidedSender voided the envelope
envelope.expiredEnvelope reached expiry
recipient.viewedRecipient opened document
recipient.signedRecipient completed signing
recipient.declinedRecipient declined to sign
Payload Format
{
"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
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
| Plan | API Envelopes / Month | Rate Limit |
|---|---|---|
| Free | 5 | 60 req/min |
| Pro | 100 | 60 req/min |
| Business | 500 | 60 req/min |
| Enterprise | Unlimited | Custom |
Test/sandbox keys are exempt from monthly quotas but still subject to rate limits.
Error Codes
| Code | Meaning |
|---|---|
| 400 | Invalid request — check the request body |
| 401 | Missing or invalid API key |
| 403 | Account suspended or feature not available |
| 404 | Resource not found |
| 422 | Validation error — details in response |
| 429 | Rate limit or quota exceeded |
| 500 | Internal 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.