REST API · WEBHOOKS · v1

Build with BuiltSign

Integrate legally binding e-signatures directly into your own application. Upload PDFs, send signing requests, place signature fields, and receive webhook events, all via a simple REST API.

Base URL
url
https://api.builtsign.com/v1
Get API Key

Settings → API Keys

Quick Start

Get a document signed in 4 API calls.

1. Create an API key

Go to Settings → API Keys in your BuiltSign dashboard and create a key with write scope. Store it securely,it is shown only once.

Settings → API Keys

2. Upload a PDF

Upload any PDF via multipart form data. You'll receive a document_id to use in the next step. File size limits depend on your plan: 80 MB on Pro, 250 MB on Business, 1 GB on Enterprise.

bash
curl -X POST https://api.builtsign.com/v1/documents/upload \
  -H "X-API-Key: bsk_live_xxxxxxxxxxxxxxxxxxxx" \
  -F "file=@contract.pdf"

3. Create a signing request

Create a signing request with the document_id and a list of signers. Set auto_send: false to place fields first.

bash
curl -X POST https://api.builtsign.com/v1/signing-requests \
  -H "X-API-Key: bsk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "document_id": "doc_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "signers": [
      { "name": "Jan de Vries", "email": "jan@example.com" }
    ],
    "message": "Graag de offerte ondertekenen.",
    "expires_in_days": 7,
    "auto_send": false
  }'

4. Add fields and send

Place signature fields using percentage-based coordinates, then send to trigger the invitation email.

curl -X POST https://api.builtsign.com/v1/signing-requests/{request_id}/fields \
  -H "X-API-Key: bsk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": [
      {
        "signer_id": "sgn_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "field_type": "signature",
        "page_number": 1,
        "x_percent": 60.0,
        "y_percent": 82.0,
        "width_percent": 30.0,
        "height_percent": 8.0
      }
    ]
  }'

Authentication

All v1 endpoints require an API key passed in the X-API-Key request header.

bash
curl https://api.builtsign.com/v1/signing-requests \
  -H "X-API-Key: bsk_live_xxxxxxxxxxxxxxxxxxxx"

Scopes

ScopePermissions
readList and retrieve resources
writeCreate, update, and send resources
adminReserved for future use

Rate Limits

Each API key has a configurable rate limit (default: 60 requests/minute, max: 1000). Exceeding the limit returns HTTP 429.

Documents

Upload PDFs and list previously uploaded documents.

Signing Requests

Create, manage, and track signing requests.

Signature Fields

Place fields on a document that signers must fill in before they can complete the signing.

Field Positioning

Coordinates are percentage-based (0–100) relative to the page dimensions. The origin (0, 0) is the top-left corner of the page.

(0, 0)(100, 0)(0, 100)(100, 100)
signature
initials

Field Types

field_typeDescription
signatureFull signature
initialsInitials / paraaf
dateDate (auto-filled on sign)
textFree text input
checkboxCheckbox
selectDropdown (provide options[])

Webhooks

Receive real-time event notifications when something happens in BuiltSign.

Setup

Create a webhook in Settings → Webhooks. Choose which events to subscribe to. BuiltSign will send an HTTP POST to your endpoint for each event.

Events

EventTrigger
signing_request.createdSigning request created
signing_request.sentSigning request sent to signers
signing_request.completedAll signers have signed
signing_request.cancelledSigning request cancelled
signing_request.expiredSigning request expired
signer.viewedSigner opened the signing page
signer.signedSigner completed their signature
signer.declinedSigner declined to sign
signer.withdrawnSigner withdrew their signature
document.signedSigned PDF generated
signing_request.remindedReminder emails sent to pending signers
document.uploadedDocument uploaded via API
member.invitedTeam member invited to the organization

Payload Format

Every webhook POST includes these fields:

json
{
  "event": "signing_request.completed",
  "timestamp": "2026-05-21T11:00:00Z",
  "webhook_id": "wh_xxxxxxxxxxxxxxxxxxxx",
  "data": {
    "signing_request_id": "sr_xxxxxxxxxxxxxxxxxxxx",
    "completed_at": "2026-05-21T11:00:00Z",
    "all_signers": [
      { "email": "alice@example.com", "name": "Alice Smith", "signed_at": "2026-05-21T10:30:00Z" },
      { "email": "bob@example.com", "name": "Bob Jones", "signed_at": "2026-05-21T11:00:00Z" }
    ],
    "signed_pdf_s3_key": "orgs/org_xxx/signed/sr_xxx.pdf"
  }
}

Request Headers

HeaderValue
Content-Typeapplication/json
X-Webhook-SignatureHMAC-SHA256 hex digest
X-Webhook-Eventevent type (e.g. signer.signed)
X-Webhook-IDwebhook UUID
X-Webhook-Retryattempt number (only on retries)

Signature Verification

Validate every incoming webhook by verifying the HMAC-SHA256 signature. Always verify, and do not trust the payload without checking the signature.

import crypto from "crypto";

// Express middleware example
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-webhook-signature"];
  const secret    = process.env.BUILTSIGN_WEBHOOK_SECRET;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(req.body) // raw Buffer, not parsed JSON
    .digest("hex");

  if (expected !== signature) {
    return res.status(401).send("Invalid signature");
  }

  const event = JSON.parse(req.body);
  // TODO: handle event (e.g. event.event === "document.signed")
  res.sendStatus(200);
});

Retry Behavior

BuiltSign retries failed deliveries up to 3 times: after 1 minute, 5 minutes, and 15 minutes. After 10 consecutive failures the webhook is automatically paused.

Node.js SDK

Zero-dependency TypeScript client for Node.js ≥ 18. Covers every API endpoint with full type safety and built-in retry logic.

Installation

bash
npm install @builtsign/node

TypeScript · Node.js ≥ 18 · Zero dependencies

Quick start

typescript
import BuiltSign from "@builtsign/node";

const builtsign = new BuiltSign({ apiKey: process.env.BUILTSIGN_API_KEY! });

// 1. Upload a PDF
const { document_id } = await builtsign.documents.upload(
  await fs.readFile("contract.pdf"),
  "contract.pdf",
);

// 2. Create a signing request
const request = await builtsign.signingRequests.create({
  document_id,
  signers: [
    { name: "Jan de Vries", email: "jan@example.com", order: 1 },
    { name: "Lisa Smit",    email: "lisa@example.com", order: 2 },
  ],
  signing_order: "sequential",
  expires_in_days: 7,
  auto_send: false,
});

// 3. Place signature fields
await builtsign.signingRequests.addFields(request.id, [{
  signer_id: request.signers[0].id,
  field_type: "signature",
  page_number: 1,
  x_percent: 60,
  y_percent: 82,
  width_percent: 30,
  height_percent: 8,
}]);

// 4. Send
await builtsign.signingRequests.send(request.id);

API resources

ResourceMethods
builtsign.documentsupload(), list()
builtsign.signingRequestscreate(), list(), get(), send(), cancel(), addFields(), download(), remind(), reactivate(), embeddedUrl()
builtsign.templateslist(), create(), get(), update(), delete(), send()
builtsign.contactslist(), create(), delete()
builtsign.webhooksverify()
builtsign.eventslist()
builtsign.bulksend(), getJob()
builtsign.usageget()

MCP Server

Let AI assistants upload documents and send signing requests directly using the Model Context Protocol.

What is MCP?

The Model Context Protocol (MCP) is an open standard by Anthropic that lets AI assistants like Claude call external tools. The BuiltSign MCP server exposes 11 tools so an AI can manage your signing workflow end-to-end.

Installation

bash
# Build from source
cd packages/mcp-server
npm install && npm run build

Claude Desktop config

json
{
  "mcpServers": {
    "builtsign": {
      "command": "node",
      "args": ["/path/to/packages/mcp-server/dist/index.js"],
      "env": {
        "BUILTSIGN_API_KEY": "bs_live_xxxxxxxxxxxxxxxxxxxx",
        "BUILTSIGN_API_URL": "https://app.builtsign.com"
      }
    }
  }
}

Available tools

upload_documentread a local PDF and upload it
list_documents
create_signing_requestcreate draft
create_and_send_signing_requestcreate and send in one step
send_signing_requestsend an existing draft
get_signing_requestcheck status
list_signing_requestswith status filter and cursor pagination
cancel_signing_request
send_reminder
list_templates
send_from_templatemap roles to real signers
list_eventsfull audit trail with cursor pagination

Bring Your Own AI (BYOAI)

Connect your own AI pipeline to BuiltSign. After every completed signing, receive a webhook with a 1-hour download link to the signed PDF — process it entirely on your own infrastructure.

Enterprise only

How it works

01

Document signed

All signers complete the signing request. BuiltSign generates the final PDF and stores it securely on S3.

02

Webhook fires

BuiltSign immediately POSTs a JSON payload to your configured endpoint, including a 1-hour presigned URL to download the signed PDF.

03

Your AI processes

Your pipeline downloads the PDF and does whatever you need: extract data, classify, archive, trigger workflows. BuiltSign never sees the output.

What you can build

Extract contract data (names, amounts, dates) and push to CRM or ERP

Trigger downstream workflows: onboarding, provisioning, compliance checks

Send Slack notifications with an AI-generated summary of the signed document

Feed signed contracts into a RAG pipeline for internal document search

Setup

BYOAI is configured per organization. You provide an HTTPS endpoint and a signing secret.

1

Go to Settings → Developers → Bring Your Own AI

2

Enter your HTTPS endpoint URL and generate a signing secret

3

Deploy your webhook handler. Use the signing secret to verify each request (see code below)

Open AI integration settings

Webhook payload

BuiltSign sends a POST request with this JSON body. The signed_pdf_url is a presigned S3 URL valid for 1 hour.

json
{
  "event": "document.signed",
  "signing_request_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "document_name": "Koopcontract 2026.pdf",
  "completed_at": "2026-06-16T10:30:00Z",
  "signed_pdf_url": "https://s3.amazonaws.com/builtsign/signed/...?X-Amz-Expires=3600",
  "expires_in": 3600,
  "signers": [
    {
      "name": "Jan de Vries",
      "email": "jan@example.com",
      "signed_at": "2026-06-16T10:28:00Z"
    },
    {
      "name": "Lisa Smit",
      "email": "lisa@example.com",
      "signed_at": "2026-06-16T10:30:00Z"
    }
  ]
}

signed_pdf_url expires after 1 hour. Download the PDF immediately in your handler.

Verify X-BuiltSign-Signature on every request. Reject anything that doesn't match.

Verifying the signature and downloading the PDF

Always verify the HMAC-SHA256 signature before processing the payload. Use the raw request body (not parsed JSON) for the HMAC calculation.

import crypto from "crypto";
import express from "express";

app.post("/ai-webhook", express.raw({ type: "application/json" }), async (req, res) => {
  // 1. Verify the signature
  const signature = req.headers["x-builtsign-signature"];
  const secret    = process.env.BUILTSIGN_AI_SECRET;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(req.body)          // raw Buffer — do NOT parse first
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
    return res.status(401).send("Invalid signature");
  }

  const payload = JSON.parse(req.body);

  // 2. Download the signed PDF (URL valid 1 hour)
  const pdfResponse = await fetch(payload.signed_pdf_url);
  const pdfBuffer   = await pdfResponse.arrayBuffer();

  // 3. Send to your AI pipeline
  await yourAiPipeline.process({
    documentName: payload.document_name,
    pdf:          Buffer.from(pdfBuffer),
    signers:      payload.signers,
    completedAt:  payload.completed_at,
  });

  res.sendStatus(200);
});

Request headers

HeaderValue
Content-Typeapplication/json
X-BuiltSign-SignatureHMAC-SHA256 hex digest of raw body
X-BuiltSign-Eventdocument.signed

Ready to connect your AI?

Configure your endpoint in Settings. Takes under 2 minutes.

Configure now

Embedded Signing

Let signers sign documents directly inside your application. No redirect to builtsign.com.

1. Generate an embedded URL (server-side)

Call embeddedUrl() from your backend with the allowed origin of your frontend. Returns a short-lived URL per signer.

typescript
const { signers } = await builtsign.signingRequests.embeddedUrl(
  request.id,
  { allowed_origin: "https://yourapp.com" },
);

// Pass signer.embedded_url to your frontend
const signerUrl = signers[0].embedded_url;

2. Mount the signing iframe (client-side)

Install @builtsign/embed and pass the URL to BuiltSignEmbed. It creates an iframe, listens for postMessage events, and calls your callbacks.

bash
npm install @builtsign/embed
typescript
import { BuiltSignEmbed } from "@builtsign/embed";

const embed = new BuiltSignEmbed({
  containerId: "signing-container",  // <div id="signing-container">
  embeddedUrl: signerUrl,

  onSigned: (e) => {
    console.log("Signed!", e.documentName);
    embed.close();
  },
  onDeclined: (e) => {
    console.log("Declined:", e.reason);
    embed.close();
  },
});

embed.open();

Enterprise

Built for enterprise teams

Need white-label branding, SSO, or a dedicated environment? BuiltSign is a SaaS product, no self-installation required. Contact us to discuss enterprise onboarding, custom contracts, and advanced integrations.

  • White-label API & signing UI
  • SSO (SAML 2.0 / OIDC)
  • Custom domain
  • Dedicated environment
  • SLA + priority support
  • Custom rate limits & volume pricing
Contact sales

Error Codes

All errors return a JSON body with a detail field describing the problem.

CodeDescription
400Bad request, invalid input or constraint violation
401Unauthorized, missing or invalid API key
403Forbidden, insufficient scope or plan restriction
404Not found, resource does not exist or belongs to another org
409Conflict, action not allowed in current state
422Unprocessable, validation error (check field details)
429Too many requests, rate limit exceeded
502Upstream error, S3 or email provider temporarily unavailable