Overview
Verafirma is a high-throughput eSignature API built for developers. Send legally binding documents for signature, track who has signed, and download completed PDFs — all via a clean REST API. It is designed for production use cases like HR onboarding, contract management, insurance enrollment, and any AI agent that needs to collect signatures programmatically.
Base URL: https://api.verafirma.com
Cost: $0.10 per envelope. Pay with a credit card via Stripe, or with ETH on Base via x402 — no account required for crypto payments.
Key concepts
Envelope — One PDF sent to one or more recipients for signature. The fundamental unit. Each envelope costs $0.10.
Recipient — A person who interacts with an envelope. Signers must complete fields and sign. Approvers must approve before signers are notified. CC and Viewer roles receive copies but take no action.
Field — A signature, date, name, text box, or other input placed at a specific location on the PDF. Fields tell recipients exactly where to act.
Template — A saved envelope definition. Create once from a PDF with fields already placed. Reuse forever — just supply recipient emails each time. Essential for any document sent repeatedly at scale.
Webhook — A URL you register. Verafirma calls it whenever something happens to one of your envelopes — signed, completed, rejected, expired. Your app reacts instantly without polling.
Placing fields on a PDF
You have two options:
Coordinate-based. Specify x/y positions and dimensions in the API call. All coordinates are percentages (0–100) of the page width/height. positionX: 0 is the left edge, positionY: 0 is the top.
PDF placeholders (recommended for fixed-format documents). Embed markers like {{signature, r1}} directly in your Word or Google Doc before exporting to PDF. Verafirma detects and places fields automatically on upload — no coordinates needed. r1, r2, etc. map to recipients in the order they appear in the API call.
Employee Signature: {{signature, r1}}
Employee Name: {{name, r1}}
Date Signed: {{date, r1}}
HR Approval: {{signature, r2}}
HR Name: {{name, r2}}Envelope statuses
| Status | Meaning |
|---|---|
PENDING | Created but not yet sent to recipients |
SENT | Distributed — recipients have received signing emails |
PARTIALLY_SIGNED | Some but not all recipients have signed |
COMPLETED | All recipients have signed — document is sealed |
REJECTED | A recipient actively declined to sign |
EXPIRED | Signing deadline passed |
CANCELLED | Voided by the originator |
Field types
| Type | Description | Auto-filled? |
|---|---|---|
SIGNATURE | Drawn, typed, or uploaded signature | No |
INITIALS | Recipient's initials | No |
NAME | Recipient's full name | Yes |
EMAIL | Recipient's email address | Yes |
DATE | Date the document was signed | Yes |
TEXT | Free-form text input | No |
NUMBER | Numeric input | No |
CHECKBOX | One or more checkboxes | No |
RADIO | Single choice from options | No |
DROPDOWN | Single choice from a dropdown list | No |
Recipient roles
| Role | Description |
|---|---|
SIGNER | Must complete all assigned fields and sign |
APPROVER | Must approve before signers are notified |
CC | Receives a copy — no action required |
VIEWER | Can view during signing — no action required |
Authentication
Verafirma supports two authentication methods. Use whichever fits your use case.
API Key — Sign up at app.verafirma.com, purchase prepaid credits with a credit card, and generate an API key. Pass it as a Bearer token. Credits are deducted automatically when envelopes are sent.
Authorization: Bearer vf_live_your_api_key_hereWallet / x402 — No account or signup required. Connect a MetaMask wallet, sign a challenge to prove ownership, and receive a JWT. Or pay per-envelope using the x402 protocol with a payment proof in the x-payment header. Designed for AI agents that need to operate autonomously.
Get wallet challenge
Step 1 of wallet authentication. Returns a unique nonce and a message for the wallet to sign. The nonce expires after 10 minutes. Call this first, sign the returned message with MetaMask, then submit the signature to /verify.
Query params: wallet — Ethereum address starting with 0x
curl "https://api.verafirma.com/v1/auth/challenge?wallet=0xabc..."{
"nonce": "a3f8c2d1e4b5...",
"message": "Sign this message to authenticate with Verafirma.\nNonce: a3f8c2d1e4b5...",
"expiresAt": "2026-04-11T03:00:00.000Z"
}Error responses: INVALID_WALLET_ADDRESS.
Verify signature and issue JWT
Step 2 of wallet authentication. Submit the wallet address, nonce, and the signature. Returns a JWT valid for 1 hour. Use this as a Bearer token on subsequent requests.
Body: JSON — { walletAddress, nonce, signature }
curl -X POST "https://api.verafirma.com/v1/auth/verify" \
-H "Content-Type: application/json" \
-d '{"walletAddress":"0xabc...","nonce":"a3f8c2...","signature":"0x..."}'{
"token": "eyJhbGciOiJIUzI1NiJ9...",
"walletAddress": "0xabc...",
"expiresIn": 3600,
"expiresAt": "2026-04-11T03:00:00.000Z"
}Error responses: MISSING_FIELDS, INVALID_NONCE, CHALLENGE_EXPIRED, NONCE_ALREADY_USED, INVALID_SIGNATURE.
Envelopes
An envelope is a PDF document sent to one or more recipients for signature. When sent, each recipient receives an email with a secure link. If you set signingOrder, recipients sign in sequence — recipient 2 is only notified after recipient 1 signs.
There are two ways to create an envelope. The simple workflow creates and sends in one call. The draft workflow lets you create first, add fields programmatically, then send — useful when field placement depends on data not known at creation time.
Create and send envelope
Creates an envelope and immediately sends signing emails to all recipients in one call. This is the standard workflow for most use cases. Supports idempotency via Idempotency-Key — safe to retry without creating duplicates. Use X-Verafirma-Test-Mode: true for free test sends with no real emails.
Leave fields as an empty array and embed {{signature, r1}} markers in your PDF to use placeholder-based field placement instead of coordinates.
Body: multipart/form-data — pdf (the file) + payload (JSON string)
curl -X POST "https://api.verafirma.com/v1/envelopes" \
-H "Authorization: Bearer vf_live_your_key" \
-H "Idempotency-Key: hire-john-2026-04-11" \
-F "pdf=@./contract.pdf" \
-F 'payload={
"title": "Employment Agreement — John Smith",
"storageExpiryDays": 90,
"recipients": [
{
"email": "john.smith@example.com",
"name": "John Smith",
"role": "SIGNER",
"signingOrder": 1,
"fields": [
{ "type": "SIGNATURE", "page": 1, "positionX": 10, "positionY": 80, "width": 30, "height": 8 },
{ "type": "DATE", "page": 1, "positionX": 60, "positionY": 80, "width": 20, "height": 8 }
]
},
{
"email": "hr@example.com",
"name": "HR Manager",
"role": "SIGNER",
"signingOrder": 2,
"fields": [
{ "type": "SIGNATURE", "page": 1, "positionX": 10, "positionY": 90, "width": 30, "height": 8 }
]
}
]
}'{
"envelopeId": "cmntpqhvf0001owr2wu8i4jza",
"status": "PENDING",
"title": "Employment Agreement — John Smith",
"recipients": [
{ "email": "john.smith@example.com", "name": "John Smith", "role": "SIGNER", "signingOrder": 1, "status": "PENDING" },
{ "email": "hr@example.com", "name": "HR Manager", "role": "SIGNER", "signingOrder": 2, "status": "PENDING" }
],
"storageExpiresAt": "2026-07-10T02:25:49.562Z",
"createdAt": "2026-04-11T02:25:49.563Z"
}Error responses: MISSING_PDF, MISSING_PAYLOAD, INVALID_JSON, VALIDATION_ERROR, INSUFFICIENT_CREDITS, DOCUMENSO_UNAVAILABLE.
Create draft envelope
Creates an envelope without sending it. Use this when you need to add fields programmatically after creation. The draft workflow is: create draft → add fields → send. No emails are sent until you call POST /v1/envelopes/{id}/send.
Body: Same multipart/form-data format as POST /v1/envelopes. Pass empty fields: [] for each recipient.
curl -X POST "https://api.verafirma.com/v1/envelopes/draft" \
-H "Authorization: Bearer vf_live_your_key" \
-F "pdf=@./contract.pdf" \
-F 'payload={
"title": "Employment Agreement — Jane Doe",
"recipients": [
{ "email": "jane.doe@example.com", "name": "Jane Doe", "role": "SIGNER", "signingOrder": 1, "fields": [] }
],
"storageExpiryDays": 90
}'{
"envelopeId": "cmnuduv3c0001pf0glp8g0979",
"status": "DRAFT",
"title": "Employment Agreement — Jane Doe",
"documensoNumericId": "21",
"recipients": [
{ "email": "jane.doe@example.com", "name": "Jane Doe", "role": "SIGNER", "signingOrder": 1 }
],
"createdAt": "2026-04-11T02:25:49.563Z"
}Get live envelope status
The most important status endpoint. Fetches real-time status directly from the signing service. Shows exactly who has signed, who has opened the document, and precise timestamps for each action. Also automatically corrects Verafirma's database if a webhook was missed — so this is always authoritative.
Use this to power a dashboard showing "John signed at 9:15am — HR Manager has not signed yet."
curl "https://api.verafirma.com/v1/envelopes/cmntpqhvf0001owr2wu8i4jza/status" \
-H "Authorization: Bearer vf_live_your_key"{
"envelopeId": "cmntpqhvf0001owr2wu8i4jza",
"title": "Employment Agreement — John Smith",
"status": "COMPLETED",
"createdAt": "2026-04-11T02:25:49.563Z",
"completedAt": "2026-04-11T02:26:20.440Z",
"recipients": [
{
"name": "John Smith",
"email": "john.smith@example.com",
"role": "SIGNER",
"signingOrder": 1,
"status": "SIGNED",
"signingStatus": "SIGNED",
"readStatus": "OPENED",
"signedAt": "2026-04-11T02:26:20.424Z",
"viewedAt": "2026-04-11T02:26:10.413Z",
"rejectedAt": null,
"rejectionReason": null
}
]
}Error responses: ENVELOPE_NOT_FOUND.
Get envelope
Returns envelope details and signing event history from Verafirma's database. For real-time accuracy use /status instead — this endpoint returns cached data.
curl "https://api.verafirma.com/v1/envelopes/cmntpqhvf0001owr2wu8i4jza" \
-H "Authorization: Bearer vf_live_your_key"{
"envelopeId": "cmntpqhvf0001owr2wu8i4jza",
"status": "COMPLETED",
"title": "Employment Agreement — John Smith",
"createdAt": "2026-04-11T02:25:49.563Z",
"updatedAt": "2026-04-11T02:26:20.440Z",
"recipients": [
{ "email": "john.smith@example.com", "name": "John Smith", "role": "SIGNER", "status": "SIGNED" }
],
"events": [
{ "eventType": "DOCUMENT_SENT", "recipientEmail": "john.smith@example.com", "occurredAt": "2026-04-11T02:25:50.000Z" },
{ "eventType": "DOCUMENT_COMPLETED", "recipientEmail": "john.smith@example.com", "occurredAt": "2026-04-11T02:26:20.000Z" }
]
}Error responses: ENVELOPE_NOT_FOUND.
List envelopes
Returns a paginated list of envelopes. Filter by status to find envelopes that need chasing, or by wallet address to retrieve all envelopes for a specific originator.
Query params: status, wallet, limit (max 100, default 20), offset
curl "https://api.verafirma.com/v1/envelopes?status=PARTIALLY_SIGNED&limit=20" \
-H "Authorization: Bearer vf_live_your_key"{
"data": [
{
"envelopeId": "cmntpqhvf0001owr2wu8i4jza",
"status": "PARTIALLY_SIGNED",
"title": "Employment Agreement — John Smith",
"createdAt": "2026-04-11T02:25:49.563Z",
"recipients": [
{ "email": "john.smith@example.com", "status": "SIGNED" },
{ "email": "hr@example.com", "status": "PENDING" }
]
}
],
"total": 1,
"limit": 20,
"offset": 0
}Delete envelope
Voids and cancels an envelope. Unsigned recipients can no longer sign. Completed envelopes cannot be deleted. Use this if you sent to the wrong recipient or need to start over.
curl -X DELETE "https://api.verafirma.com/v1/envelopes/cmntpqhvf0001owr2wu8i4jza" \
-H "Authorization: Bearer vf_live_your_key"{ "success": true }Error responses: ENVELOPE_NOT_FOUND, CANNOT_DELETE_COMPLETED.
Resend to unsigned recipients
Sends a reminder email to everyone who hasn't signed yet. Use this to chase signers automatically without voiding and recreating the envelope — the original audit trail is preserved.
curl -X POST "https://api.verafirma.com/v1/envelopes/cmntpqhvf0001owr2wu8i4jza/resend" \
-H "Authorization: Bearer vf_live_your_key"{
"success": true,
"resentTo": [
{ "name": "HR Manager", "email": "hr@example.com" }
]
}Error responses: ENVELOPE_NOT_FOUND, ENVELOPE_ALREADY_COMPLETE, ENVELOPE_CANCELLED, ALL_SIGNED.
Download signed PDF
Streams the completed signed PDF. Only available once all recipients have signed (status: COMPLETED). Download and store in your own system — Verafirma storage expires per your storageExpiryDays setting. Pass ?version=original for the unsigned original.
Query params: version — signed (default) or original
curl -o signed-agreement.pdf \
"https://api.verafirma.com/v1/envelopes/cmntpqhvf0001owr2wu8i4jza/download" \
-H "Authorization: Bearer vf_live_your_key"Returns: application/pdf binary stream.
Error responses: ENVELOPE_NOT_FOUND, ENVELOPE_NOT_COMPLETE, DOWNLOAD_FAILED.
Send a draft envelope
The final step of the draft workflow. After creating a draft and adding fields, call this to send to all recipients. You can optionally override the email subject and message at send time.
Body: JSON — optional meta with subject, message, redirectUrl
curl -X POST "https://api.verafirma.com/v1/envelopes/cmnuduv3c0001pf0glp8g0979/send" \
-H "Authorization: Bearer vf_live_your_key" \
-H "Content-Type: application/json" \
-d '{"meta":{"subject":"Your offer letter is ready","message":"Please sign within 5 business days."}}'{
"envelopeId": "cmnuduv3c0001pf0glp8g0979",
"status": "SENT",
"recipients": [
{ "name": "Jane Doe", "email": "jane.doe@example.com", "signingUrl": "https://sign.verafirma.com/sign/abc123" }
]
}Error responses: ENVELOPE_NOT_FOUND, ALREADY_COMPLETED, ENVELOPE_CANCELLED, SEND_FAILED.
Fields
Fields define where recipients need to sign, date, or enter information on a PDF. In the simple workflow, fields are specified at envelope creation time. In the draft workflow, fields are added after creation but before sending. Fields cannot be changed after an envelope has been sent.
Each field is assigned to a specific recipient by email address and placed at a percentage-based x/y position on a specific page.
Add fields to a draft envelope
Adds one or more fields to a draft envelope before it is sent. Specify which recipient each field belongs to by their email address — Verafirma looks up their internal ID automatically.
Body: JSON with a fields array
curl -X POST "https://api.verafirma.com/v1/envelopes/cmnuduv3c0001pf0glp8g0979/fields" \
-H "Authorization: Bearer vf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"fields": [
{ "type": "SIGNATURE", "recipientEmail": "jane.doe@example.com", "page": 1, "positionX": 10, "positionY": 80, "width": 30, "height": 8 },
{ "type": "DATE", "recipientEmail": "jane.doe@example.com", "page": 1, "positionX": 60, "positionY": 80, "width": 20, "height": 8 }
]
}'{
"fields": [
{ "fieldId": 101, "type": "SIGNATURE", "page": 1, "positionX": 10, "positionY": 80, "width": 30, "height": 8 },
{ "fieldId": 102, "type": "DATE", "page": 1, "positionX": 60, "positionY": 80, "width": 20, "height": 8 }
]
}Error responses: ENVELOPE_NOT_FOUND, RECIPIENT_NOT_FOUND, FIELD_CREATION_FAILED.
Remove a field
Removes a field from a draft envelope. The fid is the numeric field ID returned when the field was created. Only works before the envelope is sent.
curl -X DELETE "https://api.verafirma.com/v1/envelopes/cmnuduv3c0001pf0glp8g0979/fields/101" \
-H "Authorization: Bearer vf_live_your_key"{ "success": true }Error responses: ENVELOPE_NOT_FOUND, INVALID_FIELD_ID, DELETE_FAILED.
Templates
Templates are how you achieve high throughput. Instead of re-uploading your health insurance form and re-specifying field positions for every new employee, you create a template once and reuse it forever. Each use just needs the recipient's name and email — the PDF and all field placements are already saved.
Recommended workflow
1. Embed {{signature, r1}} markers in your Word or Google Doc, then export to PDF.
2. Upload via POST /v1/templates — fields are detected automatically. The response shows which fields were found so you can verify before using in production.
3. Call POST /v1/envelopes/from-template with just recipient emails whenever you need to send. The template handles everything else.
Create template
Uploads a PDF and creates a reusable template. If the PDF contains {{placeholder}} markers, fields are detected and placed automatically. The response includes all detected fields — no second call needed to verify. The detectedFromPlaceholders flag tells you whether auto-detection ran.
Body: multipart/form-data — file (PDF) + payload (JSON string)
curl -X POST "https://api.verafirma.com/v1/templates" \
-H "Authorization: Bearer vf_live_your_key" \
-F "file=@./health-insurance-form.pdf" \
-F 'payload={
"title": "Health Insurance Enrollment Form",
"recipients": [
{ "email": "employee@example.com", "name": "Employee", "role": "SIGNER", "signingOrder": 1 },
{ "email": "hr@example.com", "name": "HR Manager", "role": "SIGNER", "signingOrder": 2 }
]
}'{
"templateId": "42",
"title": "Health Insurance Enrollment Form",
"recipients": [
{ "id": 1, "email": "employee@example.com", "name": "Employee", "role": "SIGNER", "signingOrder": 1 },
{ "id": 2, "email": "hr@example.com", "name": "HR Manager", "role": "SIGNER", "signingOrder": 2 }
],
"fields": [
{ "fieldId": 101, "type": "SIGNATURE", "page": 1, "positionX": 10, "positionY": 75, "width": 30, "height": 8, "recipientId": 1 }
],
"detectedFromPlaceholders": true,
"createdAt": "2026-04-11T02:25:49.563Z"
}Error responses: UNAUTHORIZED, MISSING_FILE, MISSING_PAYLOAD, VALIDATION_ERROR, DOCUMENSO_UNAVAILABLE.
List templates
Returns all templates for your account or wallet.
Query params: page (default 1), perPage (max 100, default 20)
curl "https://api.verafirma.com/v1/templates" \
-H "Authorization: Bearer vf_live_your_key"{
"data": [{ "templateId": "42", "title": "Health Insurance Enrollment Form", "type": "PRIVATE", "createdAt": "2026-04-11T02:25:49.563Z" }],
"total": 1, "page": 1, "perPage": 20
}Get template
Returns full template detail including all recipients and field positions. Use this to verify placeholder detection worked correctly after template creation.
curl "https://api.verafirma.com/v1/templates/42" \
-H "Authorization: Bearer vf_live_your_key"Returns the full template object with recipients and fields arrays.
Error responses: UNAUTHORIZED, INVALID_ID, TEMPLATE_NOT_FOUND.
Update template
Updates a template's title or default email settings. All fields are optional.
curl -X PUT "https://api.verafirma.com/v1/templates/42" \
-H "Authorization: Bearer vf_live_your_key" \
-H "Content-Type: application/json" \
-d '{"title":"Health Insurance Form v2","meta":{"subject":"Sign your health insurance form"}}'{ "templateId": "42", "title": "Health Insurance Form v2", "updatedAt": "2026-04-11T03:00:00.000Z" }Error responses: UNAUTHORIZED, INVALID_ID, UPDATE_FAILED.
Delete template
Permanently deletes a template. Envelopes already sent from this template are not affected.
curl -X DELETE "https://api.verafirma.com/v1/templates/42" \
-H "Authorization: Bearer vf_live_your_key"{ "success": true }Create envelope from template
The core high-throughput call. Uses a saved template to create and send an envelope. Supply only recipient details — the PDF and all field positions come from the template. Map real people to template recipient slots using the id values returned when the template was created.
curl -X POST "https://api.verafirma.com/v1/envelopes/from-template" \
-H "Authorization: Bearer vf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"templateId": "42",
"title": "Health Insurance — Jane Doe",
"sendImmediately": true,
"storageExpiryDays": 90,
"recipients": [
{ "id": 1, "email": "jane.doe@example.com", "name": "Jane Doe" },
{ "id": 2, "email": "hr@example.com", "name": "HR Manager" }
],
"meta": {
"subject": "Action required: sign your health insurance form",
"message": "Please complete this within 5 business days."
}
}'{
"envelopeId": "cmo1abc2def3ghi4jkl5",
"status": "SENT",
"title": "Health Insurance — Jane Doe",
"recipients": [
{ "name": "Jane Doe", "email": "jane.doe@example.com", "role": "SIGNER", "signingUrl": "https://sign.verafirma.com/sign/abc123" }
],
"createdAt": "2026-04-11T02:25:49.563Z"
}Error responses: UNAUTHORIZED, VALIDATION_ERROR, INVALID_TEMPLATE_ID, DOCUMENSO_UNAVAILABLE.
Webhooks
Webhooks let your application react to signing events in real time. Register a URL and Verafirma calls it automatically whenever something happens to one of your envelopes. When an employee signs their offer letter, your HR app webhook fires instantly — trigger the next onboarding step, notify a manager, or update your database without polling.
Webhooks work for both API key accounts and wallet-based users. Register once and receive events for all future envelopes.
Verifying webhook signatures
Every webhook request includes an X-Verafirma-Signature header with an HMAC-SHA256 signature. Always verify this before processing — it proves the call came from Verafirma.
import { createHmac } from 'crypto'
function verifyWebhook(rawBody: string, signature: string, secret: string): boolean {
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex')
return expected === signature
}
app.post('/webhooks/verafirma', (req, res) => {
const sig = req.headers['x-verafirma-signature'] as string
if (!verifyWebhook(req.rawBody, sig, process.env.WEBHOOK_SECRET!)) {
return res.status(401).send('Invalid signature')
}
const { event, envelopeId, data } = req.body
res.status(200).send('ok')
})Events
| Event | When it fires |
|---|---|
envelope.sent | Envelope distributed to recipients |
envelope.viewed | A recipient opened the document |
envelope.signed | One recipient signed (others may still be pending) |
envelope.completed | All recipients signed — document sealed and downloadable |
envelope.rejected | A recipient declined to sign |
envelope.expired | Signing deadline passed |
envelope.cancelled | Originator voided the envelope |
Payload format
{
"event": "envelope.signed",
"envelopeId": "cmntpqhvf0001owr2wu8i4jza",
"timestamp": "2026-04-11T02:26:20.424Z",
"data": {
"envelope": { "title": "Employment Agreement", "status": "PARTIALLY_SIGNED", "createdAt": "2026-04-11T02:25:49.563Z" },
"recipient": { "name": "John Smith", "email": "john.smith@example.com", "role": "SIGNER", "signedAt": "2026-04-11T02:26:20.424Z" }
}
}Retry policy
Failed deliveries retry automatically: immediately, then after 1 min, 5 min, 30 min, and 2 hours. After 5 failed attempts the delivery is marked FAILED. Use the deliveries endpoint to inspect failures.
Register webhook
Registers a URL to receive event notifications. Choose which events you want. The secret is shown only once — store it immediately. A secure secret is generated if you don't provide one.
curl -X POST "https://api.verafirma.com/v1/webhooks" \
-H "Authorization: Bearer vf_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/verafirma",
"events": ["envelope.signed", "envelope.completed", "envelope.rejected"],
"description": "HR onboarding — production"
}'{
"webhookId": "clwh1abc2def3ghi",
"url": "https://your-app.com/webhooks/verafirma",
"events": ["envelope.signed", "envelope.completed", "envelope.rejected"],
"active": true,
"secret": "a1b2c3d4... (shown once only — store immediately)",
"createdAt": "2026-04-11T02:25:49.563Z"
}Error responses: UNAUTHORIZED, VALIDATION_ERROR.
List webhooks
Returns all registered webhooks for your account or wallet.
curl "https://api.verafirma.com/v1/webhooks" \
-H "Authorization: Bearer vf_live_your_key"{
"data": [{ "webhookId": "clwh1abc2def3ghi", "url": "https://your-app.com/webhooks/verafirma", "events": ["envelope.completed"], "active": true }],
"total": 1
}Get webhook
Returns details for a single webhook. The secret is not returned after creation.
curl "https://api.verafirma.com/v1/webhooks/clwh1abc2def3ghi" \
-H "Authorization: Bearer vf_live_your_key"Error responses: UNAUTHORIZED, WEBHOOK_NOT_FOUND.
Update webhook
Updates a webhook's URL, events, description, or active status. All fields are optional. Use active: false to pause without deleting.
curl -X PUT "https://api.verafirma.com/v1/webhooks/clwh1abc2def3ghi" \
-H "Authorization: Bearer vf_live_your_key" \
-H "Content-Type: application/json" \
-d '{"active": false}'{ "webhookId": "clwh1abc2def3ghi", "active": false, "updatedAt": "2026-04-11T03:00:00.000Z" }Error responses: UNAUTHORIZED, WEBHOOK_NOT_FOUND, VALIDATION_ERROR.
Delete webhook
Permanently removes a webhook. No further events will be delivered to its URL.
curl -X DELETE "https://api.verafirma.com/v1/webhooks/clwh1abc2def3ghi" \
-H "Authorization: Bearer vf_live_your_key"{ "success": true }Webhook delivery history
Returns the delivery log — essential for debugging missed events in production. Shows each delivery attempt, the HTTP response code your server returned, and when the next retry is scheduled.
Query params: page, perPage (max 100)
curl "https://api.verafirma.com/v1/webhooks/clwh1abc2def3ghi/deliveries" \
-H "Authorization: Bearer vf_live_your_key"{
"data": [
{ "deliveryId": "cldlv1abc2def", "event": "envelope.completed", "status": "SUCCESS", "attempts": 1, "responseCode": 200, "lastAttemptAt": "2026-04-11T02:26:21.000Z", "nextRetryAt": null }
],
"total": 1, "page": 1, "perPage": 20
}Delivery statuses: PENDING, SUCCESS, FAILED.
Account
Account endpoints return information about the authenticated developer — credit balance, usage history, and envelope history. Both API key holders and wallet-based users have full access to their transaction history.
Account info
Returns your account details and current credit balance. API key holders see email, API key prefix, and credit balance. Wallet users see their wallet address and total spending.
curl "https://api.verafirma.com/v1/account" \
-H "Authorization: Bearer vf_live_your_key"{
"accountId": "cmnrqkgwd00023fy5ntu74gzp",
"email": "you@example.com",
"name": "Jane Developer",
"apiKeyPrefix": "vf_live_d7d8",
"creditsCents": 2000,
"creditsFormatted": "$20.00",
"sandboxUsed": 2,
"sandboxLimit": 5,
"createdAt": "2026-04-09T17:13:35.629Z"
}Error responses: UNAUTHORIZED, TOKEN_INVALID.
Usage history
Returns your billing ledger — every credit deduction or charge, with the associated envelope title. Useful for reconciliation and usage reporting. Most recent entries first.
Query params: limit (max 100, default 20), offset
curl "https://api.verafirma.com/v1/account/usage" \
-H "Authorization: Bearer vf_live_your_key"{
"data": [
{
"id": "cmntpqhyh0003owr2vtx7vawj",
"direction": "debit",
"amountCents": 10,
"amountFormatted": "$0.10",
"description": "Envelope creation — API key payment",
"envelopeId": "cmntpqhvf0001owr2wu8i4jza",
"envelopeTitle": "Employment Agreement — John Smith",
"txHash": null,
"createdAt": "2026-04-11T02:25:49.669Z"
}
],
"total": 5, "limit": 20, "offset": 0
}Error responses: UNAUTHORIZED, TOKEN_INVALID.
Account summary
Returns aggregate spending totals, envelope counts by status, daily and monthly spending breakdowns, and the 10 most recently active pending envelopes. Designed for building dashboard views. Requires wallet JWT authentication.
curl "https://api.verafirma.com/v1/account/summary" \
-H "Authorization: Bearer YOUR_WALLET_JWT"{
"walletAddress": "0xabc...",
"summary": { "totalEnvelopes": 42, "totalSpentCents": 420, "totalSpentFormatted": "$4.20", "byStatus": { "COMPLETED": 38, "PENDING": 4 } },
"spending": { "byDay": [{ "date": "2026-04-11", "cents": 50 }], "byMonth": [{ "month": "2026-04", "cents": 420 }] },
"pendingEnvelopes": []
}Error responses: UNAUTHORIZED, TOKEN_INVALID.
Account envelope history
Returns all envelopes for the authenticated account or wallet in reverse chronological order. Supports status filtering and pagination.
Query params: status, limit (max 100, default 20), offset
curl "https://api.verafirma.com/v1/account/envelopes?status=COMPLETED" \
-H "Authorization: Bearer vf_live_your_key"{
"data": [{ "id": "cmntpqhvf0001owr2wu8i4jza", "status": "COMPLETED", "title": "Employment Agreement — John Smith", "createdAt": "2026-04-11T02:25:49.563Z" }],
"total": 38, "limit": 20, "offset": 0
}Error responses: UNAUTHORIZED, TOKEN_INVALID.
Discovery
Machine-readable endpoints for tooling, AI agents, and API clients.
OpenAPI schema
Machine-readable OpenAPI 3.1 schema for all REST routes. Import into Postman, Insomnia, or use to generate SDKs.
curl https://api.verafirma.com/openapi.jsonLLM-readable service description
Plain text summary of Verafirma's capabilities for AI model discovery and tool context.
curl https://api.verafirma.com/llms.txtMCP discovery
Advertises Verafirma as an MCP-compatible tool server. Claude and other MCP agents use this to discover and call Verafirma automatically without manual configuration.
curl https://api.verafirma.com/.well-known/mcp.json{
"name": "verafirma",
"version": "1.0.0",
"description": "eSignature API for developers and AI agents",
"mcpUrl": "https://api.verafirma.com/mcp",
"pricing": { "per_envelope_cents": 10, "currency": "USD" }
}