Loading...
Founding Member Deal: $199 one-time for lifetime Business plan — 3 of 100 spots claimed. Claim your spot →
Loading...
Integrate legally binding e-signatures into your application with a single API call. Upload a document, attach a signature, get back a signed PDF with an embedded audit trail.
6 endpoints. Sign, template, send, track. Official Node.js SDK included.
API key auth. TLS encryption. Audit trail on every document.
PDF, PNG, JPG. Draw, type, stamp, brand signatures.
The official signbolt Node.js package wraps every v1 endpoint with full TypeScript types, proper error handling, and multipart upload support. Works in Node 18+ with built-in fetch.
Install
Node 18+ requirednpm install signboltQuick start
import { SignBolt } from 'signbolt';
import fs from 'fs';
const sb = new SignBolt('sb_live_your_key_here');
// Sign a contract with a typed signature
const result = await sb.sign('./contract.pdf', {
mode: 'type',
typedName: 'Jane Doe',
signerName: 'Jane Doe',
signerEmail: 'jane@example.com',
positions: [{ x: 75, y: 88, page: 0, width: 20 }],
});
fs.writeFileSync('contract-signed.pdf', result.pdf);
console.log('Audit ID:', result.auditId);
console.log('Signed at:', result.signedAt);.d.ts declarations for every method, option, and return type. Import types such as SignOptions, Position, and SignBoltError directly from signbolt.Every API request must include a Bearer token in the Authorization header. API keys are scoped to your account and tied to your Business plan subscription.
Step 1 — Generate a key in your dashboard
Go to Dashboard → API Keys and click Generate New Key. Copy the key immediately — it is shown only once.
Step 2 — Include the key in every request
Authorization: Bearer sb_live_xxxxxxxxxxxxxxxxxxxxKey format
Keys are prefixed with sb_live_ for production. Keys are stored as SHA-256 hashes — if you lose a key, revoke it and generate a new one.
The core signing endpoint. Send a document and signature data via multipart/form-data and receive a signed PDF with an embedded audit trail.
https://signbolt.store/api/v1/sign| Field | Type | Required | Description |
|---|---|---|---|
| file | File | Yes | PDF, PNG, or JPG to sign. Max 25 MB. |
| signatureData | String | Yes* | Base64 data URI of the signature image (e.g. data:image/png;base64,...) |
| signerName | String | Yes | Full name of the signer. Embedded in the audit trail. |
| signerEmail | String | Yes | Email address of the signer. Embedded in the audit trail. |
| x | Number | No | Horizontal position as % of page width (0–100). Default: 50. |
| y | Number | No | Vertical position as % of page height (0–100). Default: 85. |
| page | Number | No | Page number to sign (0-indexed). Default: 0 (first page). |
| width | Number | No | Signature width as % of page width (10–60). Default: 25. |
| mode | String | No | draw, type, or upload. Default: draw. |
| typedName | String | Yes* | Name to render as text signature (required when mode=type). |
| positions | JSON | No | Array of position objects for multi-page or multi-sig placement. |
| sigColor | String | No | Hex color for typed signatures. Default: #1e3a8a. |
* signatureData is required for draw/upload mode. typedName is required for type mode. When positions is provided, the individual x, y, page, and width fields are ignored.
Example request body (positions array)
// positions field — JSON stringified before appending to FormData
[
{
"x": 50, // 50% from left edge
"y": 85, // 85% from top edge
"page": 0, // First page (zero-indexed)
"width": 25 // 25% of page width
}
]
// Multi-signature example (two signatories on one document)
[
{ "x": 25, "y": 90, "page": 0, "width": 20 },
{ "x": 75, "y": 90, "page": 0, "width": 20 }
]
// Multi-page example (sign page 1, initials on page 3)
[
{ "x": 50, "y": 85, "page": 0, "width": 30 },
{ "x": 85, "y": 92, "page": 2, "width": 12 }
]Positions use percentages of the page dimensions, making placement resolution-independent. The origin (0, 0) is the top-left corner. A value of x: 100, y: 100 maps to the bottom-right corner.
x% from left edge (0 = left, 100 = right)y% from top edge (0 = top, 100 = bottom)pagePage index, zero-based (0 = first page)widthSignature width as % of page width (10–60)Common placement patterns
// Bottom-right signature block (standard legal placement)
{ "x": 75, "y": 88, "page": 0, "width": 20 }
// Centered at bottom of page
{ "x": 50, "y": 88, "page": 0, "width": 25 }
// Bottom-left (typical for invoices)
{ "x": 20, "y": 88, "page": 0, "width": 20 }
// Small initials top-right of every page
{ "x": 90, "y": 5, "page": 0, "width": 8 }
// Full-width signature for image documents
{ "x": 50, "y": 85, "page": 0, "width": 50 }The simplest way to test the API. Run this from your terminal to sign a PDF immediately.
# Sign a PDF with a typed name signature
curl -X POST https://signbolt.store/api/v1/sign \
-H "Authorization: Bearer sb_live_your_key_here" \
-F "file=@contract.pdf" \
-F "mode=type" \
-F "typedName=Jane Smith" \
-F "signerName=Jane Smith" \
-F "signerEmail=jane@example.com" \
-F 'positions=[{"x":75,"y":88,"page":0,"width":20}]' \
-o signed-contract.pdf \
-D - # print response headers so you can capture X-Audit-IdUsing the built-in fetch API (Node 18+) and the native FormData class. No third-party SDK required.
import fs from "fs";
import path from "path";
async function signDocument(filePath, signerName, signerEmail) {
const form = new FormData();
// Attach the PDF
const fileBuffer = fs.readFileSync(filePath);
const blob = new Blob([fileBuffer], { type: "application/pdf" });
form.append("file", blob, path.basename(filePath));
// Signer details
form.append("signerName", signerName);
form.append("signerEmail", signerEmail);
// Signature type and placement
form.append("mode", "type");
form.append("typedName", signerName);
form.append(
"positions",
JSON.stringify([{ x: 75, y: 88, page: 0, width: 20 }])
);
const response = await fetch("https://signbolt.store/api/v1/sign", {
method: "POST",
headers: {
Authorization: "Bearer sb_live_your_key_here",
},
body: form,
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Signing failed (${response.status}): ${error.error}`);
}
// Save the signed PDF
const signedBuffer = Buffer.from(await response.arrayBuffer());
const outputPath = filePath.replace(".pdf", "-signed.pdf");
fs.writeFileSync(outputPath, signedBuffer);
// Capture the audit trail ID for your records
const auditId = response.headers.get("X-Audit-Id");
const signedAt = response.headers.get("X-Signed-At");
console.log(`Signed successfully. Audit ID: ${auditId}, Signed at: ${signedAt}`);
return { outputPath, auditId, signedAt };
}
// Usage
signDocument("./contract.pdf", "Jane Smith", "jane@example.com")
.then(console.log)
.catch(console.error);Using the requests library. Install with pip install requests.
import requests
import json
def sign_document(file_path, signer_name, signer_email, api_key):
"""Sign a PDF document via the SignBolt API."""
headers = {
"Authorization": f"Bearer {api_key}",
}
positions = [
{"x": 75, "y": 88, "page": 0, "width": 20}
]
with open(file_path, "rb") as f:
files = {
"file": (file_path, f, "application/pdf"),
}
data = {
"signerName": signer_name,
"signerEmail": signer_email,
"mode": "type",
"typedName": signer_name,
"positions": json.dumps(positions),
}
response = requests.post(
"https://signbolt.store/api/v1/sign",
headers=headers,
files=files,
data=data,
)
if response.status_code != 200:
error = response.json()
raise Exception(f"Signing failed ({response.status_code}): {error['error']}")
# Save the signed PDF
output_path = file_path.replace(".pdf", "-signed.pdf")
with open(output_path, "wb") as out:
out.write(response.content)
# Capture audit trail details
audit_id = response.headers.get("X-Audit-Id")
signed_at = response.headers.get("X-Signed-At")
print(f"Signed successfully. Audit ID: {audit_id}, Signed at: {signed_at}")
return {"output_path": output_path, "audit_id": audit_id, "signed_at": signed_at}
# Usage
result = sign_document(
file_path="contract.pdf",
signer_name="Jane Smith",
signer_email="jane@example.com",
api_key="sb_live_your_key_here",
)
print(result)Success
Returns the signed PDF as application/pdf. The response body is the binary PDF file. Check the response headers for audit trail metadata.
Response headers
Content-Type: application/pdf Content-Disposition: attachment; filename="signed-document.pdf" X-Audit-Id: SB-A1B2-C3D4-E5F6 X-Signed-At: 2026-04-07T14:23:11.000Z
Error
Error responses return JSON with an error key describing what went wrong.
Error response body
{
"error": "Human-readable description of what went wrong"
}Audit trail
The X-Audit-Id header contains a unique identifier (e.g. SB-A1B2-C3D4-E5F6) that is also embedded inside the signed PDF. Store this ID in your database alongside the document record to link signing events. You can view full audit details in your dashboard. Learn more in our e-signature compliance guide.
Returns all available document templates. Use template slugs to generate pre-filled PDFs via the generate endpoint.
https://signbolt.store/api/v1/templatesNo request body. Pass your API key in the Authorization header.
{
"templates": [
{
"slug": "nda",
"name": "Non-Disclosure Agreement",
"description": "Mutual NDA for two parties",
"fields": [
{ "name": "partyA", "label": "Party A Name", "type": "text", "required": true },
{ "name": "partyB", "label": "Party B Name", "type": "text", "required": true },
{ "name": "effectiveDate", "label": "Effective Date", "type": "date", "required": true }
]
},
{
"slug": "freelance-contract",
"name": "Freelance Contract",
"description": "Service agreement for freelance work",
"fields": [ ... ]
}
]
}curl https://signbolt.store/api/v1/templates \
-H "Authorization: Bearer sb_live_your_key_here"import { SignBolt } from 'signbolt';
const sb = new SignBolt('sb_live_your_key_here');
const templates = await sb.listTemplates();
templates.forEach(t => {
console.log(t.slug, '-', t.name);
// Available: nda, freelance-contract, employment-offer, lease, consulting, invoice
});Fill a template with your data and receive a generated PDF. Pass the returned Buffer directly into sign() to create a fully signed document in one workflow.
https://signbolt.store/api/v1/templates/generate| Field | Type | Required | Description |
|---|---|---|---|
| slug | String | Yes | Template identifier — nda, freelance-contract, employment-offer, lease, consulting, invoice |
| fields | Object | Yes | Key/value map of field names to their filled values (strings) |
Returns the generated PDF as application/pdf binary.
curl -X POST https://signbolt.store/api/v1/templates/generate \
-H "Authorization: Bearer sb_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"slug":"nda","fields":{"partyA":"Acme Corp","partyB":"Jane Doe","effectiveDate":"2026-04-08"}}' \
-o nda.pdfimport { SignBolt } from 'signbolt';
import fs from 'fs';
const sb = new SignBolt('sb_live_your_key_here');
// Step 1 — Generate the filled PDF
const ndaBuffer = await sb.generateTemplate('nda', {
partyA: 'Acme Corp',
partyB: 'Jane Doe',
effectiveDate: '2026-04-08',
jurisdiction: 'Queensland, Australia',
});
// Step 2 — Sign it immediately (pass Buffer, not a file path)
const signed = await sb.sign(ndaBuffer, {
mode: 'type',
typedName: 'Jane Doe',
signerName: 'Jane Doe',
signerEmail: 'jane@example.com',
positions: [{ x: 75, y: 88, page: 0, width: 20 }],
});
fs.writeFileSync('nda-signed.pdf', signed.pdf);
console.log('Audit ID:', signed.auditId);List all documents signed under your account, with pagination, full-text search, date filtering, and sorting. Also supports GET /api/v1/documents/:auditId to retrieve a single document with its full audit log.
https://signbolt.store/api/v1/documents| Param | Type | Default | Description |
|---|---|---|---|
| page | Number | 1 | Page number (1-indexed) |
| limit | Number | 20 | Results per page (max 100) |
| search | String | — | Filter by file name (partial match) |
| dateFrom | String | — | ISO date — include documents signed on or after this date |
| dateTo | String | — | ISO date — include documents signed on or before this date |
| sortBy | String | signed_at | Sort field: signed_at | file_name | created_at |
| sortDir | String | desc | Sort direction: asc | desc |
{
"documents": [
{
"id": "uuid",
"fileName": "contract.pdf",
"signerName": "Jane Doe",
"signerEmail": "jane@example.com",
"signedAt": "2026-04-08T10:00:00.000Z",
"auditId": "SB-A1B2-C3D4-E5F6",
"fileHash": "sha256:abc123...",
"ipAddress": "203.0.113.1"
}
],
"total": 142,
"page": 1,
"pages": 8
}# List documents (newest first)
curl "https://signbolt.store/api/v1/documents?page=1&limit=20&sortDir=desc" \
-H "Authorization: Bearer sb_live_your_key_here"
# Get a single document by audit ID
curl "https://signbolt.store/api/v1/documents/SB-A1B2-C3D4-E5F6" \
-H "Authorization: Bearer sb_live_your_key_here"import { SignBolt } from 'signbolt';
const sb = new SignBolt('sb_live_your_key_here');
// Paginated list with filtering
const { documents, total, page, pages } = await sb.listDocuments({
page: 1,
limit: 20,
sortBy: 'signed_at',
sortDir: 'desc',
search: 'invoice', // optional filename search
dateFrom: '2026-01-01', // optional date range
dateTo: '2026-04-30',
});
console.log(`Showing page ${page} of ${pages} (${total} total)`);
documents.forEach(doc => console.log(doc.fileName, doc.auditId));
// Single document detail with audit log
const doc = await sb.getDocument('SB-A1B2-C3D4-E5F6');
console.log(doc.signerName, doc.signedAt);
doc.auditLog.forEach(e => console.log(e.event, e.timestamp));Send a document to a recipient via email. The recipient receives a secure link to view and sign the document. Signing status can be polled with the status endpoint. Requires Pro or Business plan.
https://signbolt.store/api/v1/send| Field | Type | Required | Description |
|---|---|---|---|
| file | File | Yes | PDF to send. Max 10 MB (email attachment limit). |
| recipientEmail | String | Yes | Email address of the person who needs to sign. |
| recipientName | String | Yes | Full name of the recipient. |
| subject | String | No | Email subject line. Default: 'Please sign: [filename]'. |
| message | String | No | Custom message body included in the email. |
| positions | JSON | No | Pre-configured signature placement for the recipient. |
{
"sendId": "send_a1b2c3d4",
"recipientEmail": "client@example.com",
"sentAt": "2026-04-08T10:00:00.000Z",
"status": "pending"
}curl -X POST https://signbolt.store/api/v1/send \
-H "Authorization: Bearer sb_live_your_key_here" \
-F "file=@contract.pdf" \
-F "recipientEmail=client@example.com" \
-F "recipientName=John Client" \
-F "subject=Please sign your service agreement" \
-F "message=Hi John, please review and sign at your earliest convenience." \
-F 'positions=[{"x":75,"y":88,"page":0,"width":20}]'import { SignBolt } from 'signbolt';
const sb = new SignBolt('sb_live_your_key_here');
const result = await sb.send('./contract.pdf', {
recipientEmail: 'client@example.com',
recipientName: 'John Client',
subject: 'Please sign your service agreement',
message: 'Hi John, please review and sign the attached document.',
positions: [{ x: 75, y: 88, page: 0, width: 20 }],
});
console.log('Send ID:', result.sendId); // save this for status polling
console.log('Status:', result.status); // "pending"
console.log('Sent at:', result.sentAt);Check the current signing status of a document using its audit ID. Poll this endpoint after calling send() to know when your recipient has signed.
https://signbolt.store/api/v1/status?auditId=SB-XXXX-XXXX-XXXX| Param | Type | Required | Description |
|---|---|---|---|
| auditId | String | Yes | The X-Audit-Id value returned when the document was signed or sent (e.g. SB-A1B2-C3D4-E5F6) |
// Awaiting signature
{
"auditId": "SB-A1B2-C3D4-E5F6",
"status": "pending",
"signerName": "John Client",
"signerEmail": "client@example.com",
"viewedAt": "2026-04-08T10:05:00.000Z"
}
// Signed
{
"auditId": "SB-A1B2-C3D4-E5F6",
"status": "signed",
"signerName": "John Client",
"signerEmail": "client@example.com",
"signedAt": "2026-04-08T10:12:00.000Z",
"fileHash": "sha256:abc123..."
}
// Declined
{
"auditId": "SB-A1B2-C3D4-E5F6",
"status": "declined",
"declinedAt": "2026-04-08T10:08:00.000Z"
}curl "https://signbolt.store/api/v1/status?auditId=SB-A1B2-C3D4-E5F6" \
-H "Authorization: Bearer sb_live_your_key_here"import { SignBolt } from 'signbolt';
const sb = new SignBolt('sb_live_your_key_here');
// Send document to recipient
const { sendId } = await sb.send('./contract.pdf', {
recipientEmail: 'client@example.com',
recipientName: 'John Client',
});
// Poll status every 60 seconds until signed
async function waitForSignature(auditId, maxAttempts = 60) {
for (let i = 0; i < maxAttempts; i++) {
const status = await sb.getStatus(auditId);
if (status.status === 'signed') {
console.log('Signed at:', status.signedAt);
return status;
}
if (status.status === 'declined') {
throw new Error('Document was declined');
}
// Wait 60 seconds before polling again
await new Promise(resolve => setTimeout(resolve, 60_000));
}
throw new Error('Timed out waiting for signature');
}
await waitForSignature(sendId);| Code | Status | Cause & Fix |
|---|---|---|
| 400 | Bad Request | Missing required fields or invalid field values. Check that file, signatureData or typedName, and positions are all present and correctly formatted. |
| 401 | Unauthorized | Missing or invalid API key. Ensure your Authorization header is set to Bearer sb_live_your_key_here and that the key has not been revoked. |
| 403 | Forbidden | Your account does not have API access. The REST API is a Business plan feature. Upgrade at /pricing to unlock API access. |
| 413 | Payload Too Large | The uploaded file exceeds the 25 MB size limit. Compress or split the PDF before uploading. |
| 429 | Rate Limited | Too many requests in a short window. Business plan customers get generous limits — contact us if you are hitting this consistently. |
| 500 | Server Error | An unexpected error occurred during PDF processing. This is rare. Retry the request; if it persists, contact support with the X-Audit-Id header value. |
The API is designed for production workloads. Reasonable limits are in place to protect service quality for all users.
| Plan | Documents | API calls | File size |
|---|---|---|---|
| Free | 3 / month | No API access | 10 MB |
| Pro ($8/mo) | 50 / month | No API access | 25 MB |
| Business ($24/mo) | Unlimited | Unlimited | 25 MB |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/v1/sign | Sign a document with a typed, drawn, or stamp signature | Bearer token |
| GET | /api/v1/templates | List all available document templates | Bearer token |
| POST | /api/v1/templates/generate | Generate a filled PDF from a template | Bearer token |
| GET | /api/v1/documents | List signed documents with pagination and filtering | Bearer token |
| GET | /api/v1/documents/:auditId | Get full detail for a single signed document | Bearer token |
| POST | /api/v1/send | Send a document to a recipient for signing via email | Bearer token |
| GET | /api/v1/status | Check the signing status of a document | Bearer token |
| GET | /api/v1/sign | Interactive field reference for the sign endpoint | None |
| GET | /api/keys | List your API keys | Cookie (dashboard) |
| POST | /api/keys | Create a new API key | Cookie (dashboard) |
| DELETE | /api/keys | Revoke an API key | Cookie (dashboard) |
Yes. API access is exclusive to the Business plan ($24/mo). The Business plan also includes unlimited documents, custom branding, and advanced audit trails. You can upgrade at any time from the dashboard or the pricing page.
Log in to your dashboard, navigate to the API Keys tab, and click Generate New Key. You can create multiple keys (e.g. one per integration) and revoke them individually at any time. Keys are displayed once — copy it immediately and store it securely.
PDF, PNG, JPG, and JPEG. PDF is recommended for documents that already have form fields or text. PNG/JPG are useful for image-based documents. Maximum file size is 25 MB.
Positions use percentages of the page dimensions, so your placement is resolution-independent. x: 0 is the left edge, x: 100 is the right edge. y: 0 is the top, y: 100 is the bottom. width controls how wide the signature renders as a percentage of page width. This means the same position object works correctly on A4, Letter, and any custom page size.
Yes. Every signed document gets an embedded audit trail including signer name, email, IP address, user agent, and a tamper-evident timestamp. This satisfies the requirements of Australia's Electronic Transactions Act, the US ESIGN Act, and eIDAS in the EU. Read our e-signature compliance guide for the full legal breakdown.
Explore features
What SignBolt can do beyond the API
Try the UI
Sign a document without writing a line of code
Compliance guide
Legal validity of e-signatures in AU, US, EU
One API integration — recurring value without manual work. Embed legally binding e-signatures into your SaaS, CRM, or internal tools. Install the SDK with npm install signbolt and be signing documents in minutes.
7-day free trial · Unlimited API calls · Cancel anytime