Agreement Developer API

Integrate e-signatures, AI-generated agreements, and document workflows into your application with our REST API and JavaScript SDK.

🔑 Get an API Key

Sign in, go to Admin → API Keys, and create a key. It starts with agr_.

📦 Install the SDK

Copy agreement-sdk.js into your project or load it from a <script> tag.

🚀 Make a Request

Pass your API key in the X-API-Key header on every request to /api/*.

📄

OpenAPI 3.0 Specification

Import into Postman, Insomnia, or any OpenAPI-compatible tool for instant interactive docs.

Download openapi.json

Authentication

The API supports two authentication methods. API keys are recommended for server-to-server integrations. JWT bearer tokens are used by the web app and can also be used for short-lived access.

API Key (recommended)

Create API keys from Admin → API Keys. Include the key in every request header:

curl https://yourdomain.com/api/agreements \
  -H "X-API-Key: agr_your_api_key_here"

Bearer Token (JWT)

Obtain a token via POST /api/auth/login, then pass it as a Bearer token:

curl https://yourdomain.com/api/agreements \
  -H "Authorization: Bearer eyJhbGciOiJ..."
💡
Access tokens expire after 15 minutes. Use POST /api/auth/refresh with your refresh token to get a new pair without re-authenticating.

Rate Limits

All API routes are limited to 200 requests per 15 minutes per IP address. Authentication routes have a stricter limit of 15 requests per 15 minutes.

Rate limit headers are included in every response:

HeaderDescription
RateLimit-LimitMaximum requests in the window
RateLimit-RemainingRequests remaining in the current window
RateLimit-ResetUnix timestamp when the window resets

Errors

All errors return a JSON body with success: false and an error message.

{
  "success": false,
  "error": "Agreement not found"
}
StatusMeaning
400Validation error — check your request body
401Missing or invalid credentials
403Insufficient permissions for this resource
404Resource not found
429Rate limit exceeded
500Internal server error

JavaScript / Node.js SDK

The SDK wraps the full REST API in a clean, promise-based interface. It works in both the browser and Node.js.

Installation

<script src="/js/agreement-sdk.js"></script>
curl -O https://yourdomain.com/js/agreement-sdk.js

Quick Start

const client = new AgreementSDK({ apiKey: 'agr_your_key' });

// Create a draft agreement
const { agreement } = await client.agreements.create({
  title: 'Contractor NDA — Acme Corp',
  content: '<h1>Non-Disclosure Agreement</h1><p>...</p>',
  signingOrder: 'parallel',
});

// Add signers
await client.agreements.addRecipient(agreement.id, {
  name: 'Jane Smith',
  email: 'jane@example.com',
  role: 'signer',
});

// Send for signatures
await client.agreements.send(agreement.id);
console.log('Agreement sent!', agreement.id);

AI Generation

const { content } = await client.ai.generate({
  prompt: 'NDA between Acme Corp and a freelance developer, 2-year term, California law',
  category: 'nda',
});

// Create the agreement with the generated content
const { agreement } = await client.agreements.create({
  title: 'AI-Generated NDA',
  content,
});

From Template

// List templates
const { data: templates } = await client.templates.list({ category: 'nda' });

// Create a draft from the first matching template
const { agreement } = await client.templates.useAsAgreement(templates[0].id);

Download signed PDF

// In Node.js
const url  = client.agreements.downloadUrl(agreement.id);
const resp = await fetch(url, { headers: { 'X-API-Key': 'agr_your_key' } });
const buffer = await resp.arrayBuffer();
require('fs').writeFileSync('signed.pdf', Buffer.from(buffer));

Python

No official Python package yet — use requests directly against the REST API.

import requests

API_KEY = "agr_your_key"
BASE    = "https://yourdomain.com/api"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}

# Create agreement
agreement = requests.post(f"{BASE}/agreements", json={
    "title": "Contractor NDA",
    "content": "<h1>NDA</h1><p>...</p>",
}, headers=HEADERS).json()["agreement"]

# Add recipient
requests.post(f"{BASE}/agreements/{agreement['id']}/recipients", json={
    "name": "Jane Smith",
    "email": "jane@example.com",
    "role": "signer",
}, headers=HEADERS)

# Send
requests.post(f"{BASE}/agreements/{agreement['id']}/send", headers=HEADERS)
print("Sent:", agreement["id"])

Agreements

GET /api/agreements List agreements
ParameterTypeDescription
pageintegerPage number (default: 1)
limitintegerResults per page (default: 20)
statusstringFilter: draft · pending · completed · voided · expired
searchstringSearch by title
POST /api/agreements Create a draft
FieldTypeDescription
title*stringAgreement title
contentstringHTML body. Use <!-- PAGEBREAK --> to split pages
signingOrderstringparallel (default) or sequential
expirationDatedateISO 8601 date (YYYY-MM-DD)
reminderDaysintegerDays between auto-reminders (1, 3, or 7)
messagestringMessage included in the signing email
POST /api/agreements/{id}/send Send to signers

Transitions the agreement from draftpending and dispatches signing emails to all recipients. Requires at least one signer recipient to be added first.

GET /api/agreements/{id}/download Download signed PDF

Returns the PDF binary. Available for both authenticated users (JWT/API key) and recipients via ?token= query parameter.

Recipients

POST /api/agreements/{id}/recipients Add recipient
FieldTypeDescription
name*stringFull name
email*stringEmail address
rolestringsigner · approver · cc · viewer
signingOrderintegerPosition in sequential signing order

Signature Fields

POST /api/agreements/{id}/fields Save fields

Replace all signature/form fields on the agreement. Accepts an array of field objects:

FieldTypeDescription
fieldId*stringUnique ID for this field
type*stringsignature · initials · date · text · checkbox · company · title · email · phone · dropdown · radio
recipientId*uuidAssigned recipient
x, ynumberPosition in pixels (relative to page)
width, heightnumberSize in pixels
pageNumberintegerPage the field appears on (1-indexed)
requiredbooleanWhether the field must be filled

Templates

GET /api/templates List templates
ParameterTypeDescription
categorystringFilter by category (nda, employment, service, …)
searchstringSearch by name
POST /api/templates/{id}/use Create agreement from template

Creates a new draft agreement pre-populated with the template's content and field layout. Returns the new agreement object.

AI

All AI endpoints require authentication. Results are also logged to your AI history for billing and audit purposes.

POST /api/ai/generate Generate agreement
FieldTypeDescription
prompt*stringPlain-English description of what you need
categorystringnda · employment · contractor · service · saas · real_estate · partnership · purchase · privacy · terms
agreementIduuidAttach the result to an existing agreement

Returns content (HTML string), tokensUsed, and model.

POST /api/ai/detect-risks Detect legal risks

Analyzes HTML agreement content and returns annotated HTML with <span class="risk-high">, risk-medium, and risk-low annotations.

FieldTypeDescription
content*stringHTML or plain text of the agreement

Webhooks

Agreement delivers webhook events to your HTTPS endpoint via POST. Each request is signed with HMAC-SHA256 for verification.

Signature verification

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express example
app.post('/webhooks/agreement', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-agreement-sig'];
  if (!verifyWebhook(req.body, sig, process.env.WEBHOOK_SECRET)) {
    return res.status(401).end();
  }
  const event = JSON.parse(req.body);
  console.log('Event:', event.event, event.agreementId);
  res.status(200).end();
});

Request headers

HeaderDescription
X-Agreement-EventEvent type (e.g. agreement.completed)
X-Agreement-SigHMAC-SHA256 hex signature of the raw JSON body
X-Agreement-IDAgreement UUID
Content-Typeapplication/json

Events

agreement.sent agreement.viewed agreement.signed agreement.completed agreement.voided agreement.expired recipient.signed recipient.declined

Payload structure

{
  "event": "agreement.completed",
  "agreementId": "a1b2c3d4-...",
  "timestamp": "2025-05-20T14:32:00Z",
  "data": {
    "id": "a1b2c3d4-...",
    "title": "Contractor NDA",
    "status": "completed",
    "completedAt": "2025-05-20T14:32:00Z",
    "recipients": [
      { "name": "Jane Smith", "email": "jane@example.com", "status": "signed" }
    ]
  }
}
POST /api/webhooks Create webhook
FieldTypeDescription
name*stringDescriptive name
url*stringYour HTTPS endpoint URL
events*string[]Array of event types to subscribe to
⚠️
The signing secret is returned only once in the creation response. Store it securely — it cannot be retrieved again.