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.
https://api.builtsign.com/v1Settings → API Keys
Get a document signed in 4 API calls.
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 KeysUpload 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.
curl -X POST https://api.builtsign.com/v1/documents/upload \
-H "X-API-Key: bsk_live_xxxxxxxxxxxxxxxxxxxx" \
-F "file=@contract.pdf"Create a signing request with the document_id and a list of signers. Set auto_send: false to place fields first.
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
}'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
}
]
}'All v1 endpoints require an API key passed in the X-API-Key request header.
curl https://api.builtsign.com/v1/signing-requests \
-H "X-API-Key: bsk_live_xxxxxxxxxxxxxxxxxxxx"| Scope | Permissions |
|---|---|
read | List and retrieve resources |
write | Create, update, and send resources |
admin | Reserved 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.
Upload PDFs and list previously uploaded documents.
Create, manage, and track signing requests.
Place fields on a document that signers must fill in before they can complete the signing.
Coordinates are percentage-based (0–100) relative to the page dimensions. The origin (0, 0) is the top-left corner of the page.
| field_type | Description |
|---|---|
signature | Full signature |
initials | Initials / paraaf |
date | Date (auto-filled on sign) |
text | Free text input |
checkbox | Checkbox |
select | Dropdown (provide options[]) |
Receive real-time event notifications when something happens in BuiltSign.
Create a webhook in Settings → Webhooks. Choose which events to subscribe to. BuiltSign will send an HTTP POST to your endpoint for each event.
| Event | Trigger |
|---|---|
signing_request.created | Signing request created |
signing_request.sent | Signing request sent to signers |
signing_request.completed | All signers have signed |
signing_request.cancelled | Signing request cancelled |
signing_request.expired | Signing request expired |
signer.viewed | Signer opened the signing page |
signer.signed | Signer completed their signature |
signer.declined | Signer declined to sign |
signer.withdrawn | Signer withdrew their signature |
document.signed | Signed PDF generated |
signing_request.reminded | Reminder emails sent to pending signers |
document.uploaded | Document uploaded via API |
member.invited | Team member invited to the organization |
Every webhook POST includes these fields:
{
"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"
}
}| Header | Value |
|---|---|
| Content-Type | application/json |
| X-Webhook-Signature | HMAC-SHA256 hex digest |
| X-Webhook-Event | event type (e.g. signer.signed) |
| X-Webhook-ID | webhook UUID |
| X-Webhook-Retry | attempt number (only on retries) |
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.
Zero-dependency TypeScript client for Node.js ≥ 18. Covers every API endpoint with full type safety and built-in retry logic.
npm install @builtsign/nodeTypeScript · Node.js ≥ 18 · Zero dependencies
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);| Resource | Methods |
|---|---|
| builtsign.documents | upload(), list() |
| builtsign.signingRequests | create(), list(), get(), send(), cancel(), addFields(), download(), remind(), reactivate(), embeddedUrl() |
| builtsign.templates | list(), create(), get(), update(), delete(), send() |
| builtsign.contacts | list(), create(), delete() |
| builtsign.webhooks | verify() |
| builtsign.events | list() |
| builtsign.bulk | send(), getJob() |
| builtsign.usage | get() |
Let AI assistants upload documents and send signing requests directly using the Model Context Protocol.
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.
# Build from source
cd packages/mcp-server
npm install && npm run build{
"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"
}
}
}
}| upload_document | read a local PDF and upload it |
| list_documents | |
| create_signing_request | create draft |
| create_and_send_signing_request | create and send in one step |
| send_signing_request | send an existing draft |
| get_signing_request | check status |
| list_signing_requests | with status filter and cursor pagination |
| cancel_signing_request | |
| send_reminder | |
| list_templates | |
| send_from_template | map roles to real signers |
| list_events | full audit trail with cursor pagination |
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.
Document signed
All signers complete the signing request. BuiltSign generates the final PDF and stores it securely on S3.
Webhook fires
BuiltSign immediately POSTs a JSON payload to your configured endpoint, including a 1-hour presigned URL to download the signed PDF.
Your AI processes
Your pipeline downloads the PDF and does whatever you need: extract data, classify, archive, trigger workflows. BuiltSign never sees the output.
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
BYOAI is configured per organization. You provide an HTTPS endpoint and a signing secret.
Go to Settings → Developers → Bring Your Own AI
Enter your HTTPS endpoint URL and generate a signing secret
Deploy your webhook handler. Use the signing secret to verify each request (see code below)
BuiltSign sends a POST request with this JSON body. The signed_pdf_url is a presigned S3 URL valid for 1 hour.
{
"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.
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);
});| Header | Value |
|---|---|
| Content-Type | application/json |
| X-BuiltSign-Signature | HMAC-SHA256 hex digest of raw body |
| X-BuiltSign-Event | document.signed |
Ready to connect your AI?
Configure your endpoint in Settings. Takes under 2 minutes.
Let signers sign documents directly inside your application. No redirect to builtsign.com.
Call embeddedUrl() from your backend with the allowed origin of your frontend. Returns a short-lived URL per signer.
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;Install @builtsign/embed and pass the URL to BuiltSignEmbed. It creates an iframe, listens for postMessage events, and calls your callbacks.
npm install @builtsign/embedimport { 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();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.
All errors return a JSON body with a detail field describing the problem.
| Code | Description |
|---|---|
400 | Bad request, invalid input or constraint violation |
401 | Unauthorized, missing or invalid API key |
403 | Forbidden, insufficient scope or plan restriction |
404 | Not found, resource does not exist or belongs to another org |
409 | Conflict, action not allowed in current state |
422 | Unprocessable, validation error (check field details) |
429 | Too many requests, rate limit exceeded |
502 | Upstream error, S3 or email provider temporarily unavailable |