Abundera Sign API

Create legally defensible e-signatures with cryptographic proof. Hash-chained audit trails, RFC 3161 timestamps, GitHub evidence anchoring, signer evidence scoring, signing ceremony proof, AI contract summaries, webcam identity capture, browser GPS, court-ready declarations, and public document verification, all through a simple REST API.

Base URL:https://sign.abundera.ai

Quick Start

Send a document for signature in one API call:

POST /api/v1/envelopes
Authorization: Bearer <jwt_token>

{
  "template": "nda-standard",
  "signers": [
    { "email": "signer@example.com", "name": "Jane Doe", "role": "recipient" }
  ],
  "fields": {
    "company_name": "Acme Corp",
    "effective_date": "2026-03-15"
  }
}

The signer receives an email with a secure link. When they sign, you get an HMAC-signed webhook. The completed PDF includes a Certificate of Completion, cryptographic proof page with hash-chain visualization, signer evidence scores, RFC 3161 timestamp, and a tamper-evident seal on every page.

API Resources

Interactive API Reference →OpenAPI YAMLOpenAPI JSONPostman CollectioncURL Examples

Well-known discovery

Auto-discovery endpoints under /.well-known/ (RFC 8615):

/.well-known/ indexCapabilitiesFamily API catalog →Trust posture →

Authentication

The API supports two authentication methods for authenticated endpoints: JWT Bearer tokens and product-scoped API keys.

JWT Tokens

Obtain a JWT from the Abundera auth service. JWTs are verified against the JWKS endpoint at https://abundera.ai/v1/auth/jwks.

Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

API Keys

API keys use the abnd_sign_* prefix and are passed as Bearer tokens. Create and manage API keys in the Abundera dashboard.

Authorization: Bearer abnd_sign_xxx...

API keys are product-scoped, each key is tied to Abundera Sign specifically. Keys are validated server-side against abundera.ai/v1/auth/validate-key and cached in KV for 60 seconds. Authenticated users (JWT or API key) receive 2× the per-IP rate limits. API keys are available on all tiers.

Token Refresh

Access tokens are short-lived. Use the refresh endpoint to obtain new tokens without re-authenticating:

POST/api/v1/auth/refresh

Exchange a refresh token for a new access token and refresh token pair.

Request Body
FieldTypeDescription
refresh_token requiredstringThe refresh token from your last authentication
Response
{
  "access_token": "eyJhbGciOi...",
  "refresh_token": "rt_abc123...",
  "expires_in": 900,
  "token_type": "Bearer"
}

Plan Tiers

Some endpoints and features require a specific plan tier. If your plan doesn't include access, you'll receive a 403 response with an upgrade_url.

PlanTier LevelKey Features
StarterstarterCore API, webhooks, hash-chained audit trails, RFC 3161 timestamps, Certificate of Completion, identity scoring, ceremony proof, access codes, auto reminders, public verification, bot detection
Professionalprofessional+ Bulk send, signing order, SMS OTP, AI summaries, geo-lock, identity photo capture, browser GPS, comments, GitHub anchoring, custom email branding
Businessbusiness+ Template CRUD, audit export, court-ready declarations, VPN/proxy blocking, custom retention (99yr), white label
Tier in JWT: Your plan tier is included in the JWT payload as tier. The API checks this automatically, no extra headers needed.

Auth Types

TypeUsed ByDescription
JWTEnvelope managementBearer token in Authorization header
API KeyEnvelope managementabnd_sign_* Bearer token in Authorization header
TokenSigning flowSigning token in request body or URL
PublicVerificationNo authentication required
SecretCron jobsCRON_SECRET in query parameter

Signing IDs

Signing request emails use opaque sgn_xxx IDs instead of raw hex tokens for improved security. These IDs are used in signing URLs:

https://sign.abundera.ai/sign/?token=sgn_xxxxxxxxxxxxxxxx
PropertyDetails
Formatsgn_ prefix + 16 alphanumeric characters (20 chars total)
Entropy~4.7×1028 possible values
LifetimeOne-time use, automatically revoked after signer completes signing
Storageverification_ids table with owner_type = 'signing'
Backward compatibilityLegacy raw hex tokens are still accepted

Verification IDs

Public documents use opaque vrf_xxx IDs instead of raw UUIDs. QR codes embedded in sealed PDFs link to the verification page using these IDs:

https://sign.abundera.ai/verify/?id=vrf_xxxxxxxxxxxxxxxx

The /api/v1/verify endpoint supports three lookup methods:

MethodParameterAuth Required
vrf_xxx ID?id=vrf_xxxNone (public)
Raw UUID?id=e3f8a1b2-...JWT required
SHA-256 hash?hash=a1b2c3...None (self-gating, requires knowledge of the hash)
Permanent IDs: Unlike signing IDs, verification IDs are never revoked, they provide a permanent public link to the document's verification record.

Errors

All errors return JSON with an error field. HTTP status codes follow standard conventions:

CodeMeaning
400Invalid request, check required fields
401Missing or invalid JWT
403Forbidden, not the envelope sender, or gate required
404Resource not found
409Conflict, already signed/declined
410Gone, document voided or expired
429Rate limited or tier limit exceeded
500Internal server error
// Error response format
{
  "error": "envelope_id is required"
}

Rate Limiting

API requests are rate-limited per IP address. Authenticated requests (JWT or API key) automatically receive 2× the per-IP limits using the user's identity as the rate-limit key. When rate-limited, you'll receive a 429 response.

EndpointLimit
General (all endpoints)30/min per IP (60/min for authenticated users)
POST /api/v1/envelopes10/min
POST /api/v1/orgs5/min
POST /api/v1/demo-envelope1/min
GET /api/v1/document-summary5/min
GET /api/v1/verify10/min
GET /api/v1/document-pdf3/min
Tip: Authenticated users (JWT or API key) get 2× the listed limits. For example, POST /api/v1/envelopes allows 20/min for authenticated users.

Webhooks

Abundera Sign sends HMAC-SHA256 signed webhooks to your callback_url for envelope and signer lifecycle events. Configure per-envelope callbacks or use global webhooks for all envelopes.

POST {callback_url}
Content-Type: application/json
X-Signature-256: a1b2c3d4e5...

{
  "event": "envelope.completed",
  "envelope_id": "abc-123-...",
  "completed_at": "2026-03-05T12:00:00Z"
}

Webhook Event Types

EventDescription
signer.viewedSigner opened the signing page
signer.signedSigner completed their signature
signer.declinedSigner declined to sign with a reason
signer.delegatedSigner delegated signing to another person
envelope.completedAll signers completed, sealed PDF and evidence package generated
envelope.voidedSender voided the envelope
envelope.expiredEnvelope passed its expiry date without completion
payment.completedStripe payment gate completed by signer
clause.flaggedSigner flagged a clause for negotiation
clause.respondedSender responded to a clause negotiation flag
clause.acceptedClause negotiation accepted by both parties
clause.counter_proposedSigner submitted a counter-proposal to a clause response

Verifying Webhook Signatures

Compute HMAC-SHA256(callback_secret, request_body) and compare with the X-Signature-256 header using constant-time comparison.

// Node.js verification example
const crypto = require('crypto');
const expected = crypto
  .createHmac('sha256', callbackSecret)
  .update(rawBody)
  .digest('hex');
const valid = crypto.timingSafeEqual(
  Buffer.from(expected),
  Buffer.from(signatureHeader)
);

Embedded Signing

Embed the signing experience directly in your app using an iframe. No redirects, signers complete the flow within your UI.

How it works

  1. Create an envelope with embedded: true, this skips invitation emails and returns signing_url per signer
  2. Embed the signing_url in an iframe in your app
  3. Listen for completion via webhook (signer.signed, envelope.completed) or poll the status endpoint

JavaScript (iframe)

// 1. Create envelope via your backend
const res = await fetch('/your-api/create-signing', { method: 'POST' });
const { signing_url } = await res.json();

// 2. Embed in iframe
const iframe = document.createElement('iframe');
iframe.src = signing_url;
iframe.style.cssText = 'width:100%;height:800px;border:none;border-radius:8px';
iframe.allow = 'camera;microphone';  // if using photo/video identity
document.getElementById('signing-container').appendChild(iframe);

React

function SigningEmbed({ signingUrl }) {
  return (
    <iframe
      src={signingUrl}
      style={{ width: '100%', height: '800px', border: 'none', borderRadius: '8px' }}
      allow="camera;microphone"
      title="Sign Document"
    />
  );
}

Backend: Create Embedded Envelope

// Node.js / Python / any HTTP client
const response = await fetch('https://sign.abundera.ai/api/v1/envelopes', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer abnd_sign_...',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    template: 'nda-simple',
    embedded: true,               // ← key flag
    signers: [{ name: 'Jane Doe', email: 'jane@example.com', role: 'signer' }],
    fields: { company_name: 'Acme Corp' },
  }),
});

const envelope = await response.json();
// envelope.signers[0].signing_url → embed this in your iframe

Security Notes

See code examples and framework guides →

MCP Server (AI Agents)

The Abundera Sign MCP Server lets AI coding assistants interact with the Abundera Sign API through the Model Context Protocol. Works with any MCP-compatible client (Cursor, Windsurf, VS Code, and more). Send documents for signature, check envelope status, manage templates, and review audit trails, all through natural language.

Available Tools

ToolDescription
list_templatesList templates with optional category, tag, or search filters
get_templateGet template details including fields and roles
create_envelopeCreate and send a document for e-signature
list_envelopesList envelopes with status, template, and pagination filters
get_envelopeGet full envelope details including signers and audit counts
void_envelopeVoid (cancel) an envelope and notify unsigned signers
send_reminderSend a reminder to unsigned signers
get_audit_trailGet the hash-chained audit trail for an envelope
check_usageCheck current month's envelope usage and plan limits

Setup

Add to your MCP client configuration (e.g., mcp.json or equivalent):

{
  "mcpServers": {
    "sign-abundera": {
      "command": "node",
      "args": ["/path/to/mcp/index.js"],
      "env": {
        "ABUNDERA_SIGN_API_KEY": "abnd_sign_..."
      }
    }
  }
}

Setup for Cursor / Windsurf

Add the same configuration to .cursor/mcp.json (Cursor) or your Windsurf MCP settings file.

Example

Ask your AI assistant: "Send an NDA to jane@example.com using the nda-simple template with company name Acme Corp"

The agent calls create_envelope:

{
  "template_id": "nda-simple",
  "signers": [{ "name": "Jane Doe", "email": "jane@example.com", "role": "signer" }],
  "field_values": { "company_name": "Acme Corp" }
}
Prerequisites: Node.js 18+ and an Abundera Sign API key (abnd_sign_* prefix). MCP server source and README ship in the @abundera/sign-mcp npm package.

Full endpoint reference

This page covers integration guides (authentication, webhooks, embedded signing, MCP). The complete REST API reference for every endpoint lives in our interactive docs:

Interactive API Reference →OpenAPI YAMLOpenAPI JSONPostmancURL Examples
PricingSecurity architectureManage API keys