API Documentation
Everything you need to integrate Agreements.ai into your application. Send documents for signing, manage templates, automate workflows, and react to signing events in real time.
Getting Started
Go from zero to your first API call in 5 minutes. Create an account, grab your API key, and send a document for signing.
Step 1: Create an Account
Sign up at agreements.ai/register — free, no credit card required.
Step 2: Get Your API Key
Navigate to Settings → Developers and click Create API Key. Copy the key — it starts with sk_ and is only shown once.
Store securely
Step 3: Make Your First API Call
Base URL: https://api.agreements.ai/v1
All requests require the Authorization header:
Authorization: Bearer sk_live_your_api_key_hereComplete Example: Create → Send → Check Status
# 1. Create a document
curl -X POST https://api.agreements.ai/v1/documents \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"title": "Service Agreement — Acme Corp",
"content": "<h1>Service Agreement</h1><p>This agreement is entered into between...</p>"
}'
# Response: { "id": "doc_7kXm2pQ9", "title": "Service Agreement — Acme Corp", ... }
# 2. Create an envelope (sends for signing)
curl -X POST https://api.agreements.ai/v1/envelopes \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"documentId": "doc_7kXm2pQ9",
"name": "Service Agreement — Acme Corp",
"message": "Please review and sign this agreement.",
"signers": [
{ "name": "Jane Smith", "email": "[email protected]", "role": "signer" },
{ "name": "Bob Johnson", "email": "[email protected]", "role": "signer" }
],
"fields": [
{
"type": "signature",
"signerEmail": "[email protected]",
"page": 1,
"x": 100,
"y": 500,
"width": 200,
"height": 50
},
{
"type": "signature",
"signerEmail": "[email protected]",
"page": 1,
"x": 100,
"y": 600,
"width": 200,
"height": 50
},
{
"type": "date",
"signerEmail": "[email protected]",
"page": 1,
"x": 320,
"y": 520,
"width": 100,
"height": 20
}
]
}'
# Response: { "id": "env_Qr8sT4kL", "status": "sent", ... }
# 3. Check status
curl https://api.agreements.ai/v1/envelopes/env_Qr8sT4kL \
-H "Authorization: Bearer sk_live_abc123"
# 4. Download completed document
curl https://api.agreements.ai/v1/envelopes/env_Qr8sT4kL/document \
-H "Authorization: Bearer sk_live_abc123" \
-o signed_agreement.pdfAuthentication
API Keys vs Firebase Tokens
Agreements.ai supports two authentication methods:
| Method | Format | Use Case |
|---|---|---|
| sk_live_... | API Key | Server-to-server integrations, scripts, backend automation |
| eyJhbG... | Firebase JWT | Frontend/mobile apps using Firebase Auth (short-lived) |
Both use the same header: Authorization: Bearer <token>
Creating and Managing Keys
- Go to Settings → Developers
- Click Create API Key
- Give it a descriptive name (e.g., "CRM Integration", "CI/CD Pipeline")
- Copy the key immediately — it won't be shown again
Key rotation best practices
- Rotate keys every 90 days
- Create the new key before revoking the old one
- Use separate keys for each integration
- Never hardcode keys — use environment variables
Rate Limits
Rate limits are applied per API key. Exceeding them returns 429 Too Many Requests.
| Operation | Limit | Window | Endpoints |
|---|---|---|---|
| Reads | 30 requests | Per minute | All GET endpoints |
| Writes | 10 requests | Per minute | POST, PUT, PATCH, DELETE |
| Reminders | 5 requests | Per minute | POST /envelopes/:id/remind |
Rate limit headers are included in every response: X-RateLimit-Remaining, X-RateLimit-Reset
Auth Error Responses
{
"error": "Invalid or expired API key",
"code": "AUTH_INVALID_KEY"
}{
"error": "Rate limit exceeded. Try again in 45 seconds.",
"code": "RATE_LIMIT_EXCEEDED",
"retryAfter": 45
}Core Concepts
Documents
The contract or agreement content. Created from scratch, uploaded as PDF/DOCX, or generated from a template. Documents contain the actual text that gets signed.
Envelopes
A document packaged for signing. An envelope wraps a document together with its signers, fields, and configuration. Think of it as the "transaction" — one envelope = one signing session.
Signers
Recipients who need to take action on an envelope. Each signer has a name, email, and role (signer, witness, notary, or CC). Signers receive email notifications with a unique signing link.
Fields
Interactive elements placed on the document — signature, initials, text input, date, checkbox, and dropdown. Each field is assigned to a specific signer and positioned at exact coordinates on a page.
Templates
Reusable document blueprints with merge fields like {{client_name}} and {{date}}. Use a template to generate a pre-filled document, then send it for signing.
Webhooks
Real-time HTTP callbacks that notify your server when events occur — an envelope is sent, a signer completes, an envelope is voided. Signed with HMAC-SHA256 for verification.
📊 Envelope Lifecycle
Every envelope moves through a defined set of statuses:
An envelope can also be Voided by the sender or Declined by a signer at any point after sending.
- Draft — Created but not yet sent. Signers have not been notified.
- Sent — Signing invitations delivered to all signers.
- In Progress — At least one signer has signed, but not all.
- Completed — All signers have signed. The document is finalized and tamper-sealed.
- Voided — Cancelled by the sender. No further signing possible.
- Declined — A signer refused to sign. The sender is notified with the reason.
Guide: Send Your First Document
A complete walkthrough: create a document, configure an envelope with signers and fields, send it, monitor status, and download the signed PDF.
Step 1: Create a Document
Upload your contract content. The content field accepts HTML.
curl -X POST https://api.agreements.ai/v1/documents \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"title": "Consulting Agreement",
"content": "<h1>Consulting Agreement</h1><p>This Consulting Agreement (\"Agreement\") is entered into as of {{date}} by and between {{client_name}} (\"Client\") and {{consultant_name}} (\"Consultant\").</p><h2>1. Services</h2><p>Consultant shall provide the services described in Exhibit A attached hereto.</p><h2>2. Compensation</h2><p>Client shall pay Consultant a fee of ${{amount}} per hour for services rendered.</p><h2>3. Term</h2><p>This Agreement shall commence on {{start_date}} and continue for a period of {{term_months}} months.</p><p>IN WITNESS WHEREOF, the parties have executed this Agreement:</p><p>Client: ___________________________</p><p>Consultant: ___________________________</p>"
}'{
"id": "doc_7kXm2pQ9",
"title": "Consulting Agreement",
"createdAt": "2026-02-12T10:00:00Z",
"updatedAt": "2026-02-12T10:00:00Z",
"wordCount": 156,
"status": "draft"
}Step 2: Create an Envelope
Wrap your document in an envelope with signers and field placements. The envelope is sent immediately upon creation (or set "status": "draft" to hold it).
curl -X POST https://api.agreements.ai/v1/envelopes \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"documentId": "doc_7kXm2pQ9",
"name": "Consulting Agreement — Jane Smith",
"message": "Hi Jane, please review and sign the attached consulting agreement.",
"signers": [
{
"name": "Jane Smith",
"email": "[email protected]",
"role": "signer",
"order": 1
},
{
"name": "Bob Johnson",
"email": "[email protected]",
"role": "signer",
"order": 2
}
],
"fields": [
{ "type": "signature", "signerEmail": "[email protected]", "page": 1, "x": 72, "y": 680, "width": 200, "height": 50 },
{ "type": "date", "signerEmail": "[email protected]", "page": 1, "x": 300, "y": 695, "width": 120, "height": 20 },
{ "type": "signature", "signerEmail": "[email protected]", "page": 1, "x": 72, "y": 740, "width": 200, "height": 50 },
{ "type": "date", "signerEmail": "[email protected]", "page": 1, "x": 300, "y": 755, "width": 120, "height": 20 }
],
"settings": {
"reminderDays": 3,
"expirationDays": 30
}
}'{
"id": "env_Qr8sT4kL",
"name": "Consulting Agreement — Jane Smith",
"status": "sent",
"documentId": "doc_7kXm2pQ9",
"createdAt": "2026-02-12T10:01:00Z",
"sentAt": "2026-02-12T10:01:00Z",
"expiresAt": "2026-03-14T10:01:00Z",
"signers": [
{
"id": "sig_A1b2C3",
"name": "Jane Smith",
"email": "[email protected]",
"role": "signer",
"order": 1,
"status": "pending"
},
{
"id": "sig_D4e5F6",
"name": "Bob Johnson",
"email": "[email protected]",
"role": "signer",
"order": 2,
"status": "pending"
}
]
}Step 3: Check Status
Poll the envelope to check signing progress. Each signer has their own status.
curl https://api.agreements.ai/v1/envelopes/env_Qr8sT4kL \
-H "Authorization: Bearer sk_live_abc123"Step 4: Download the Signed Document
Once the envelope status is completed, download the signed PDF with the certificate of completion embedded.
curl https://api.agreements.ai/v1/envelopes/env_Qr8sT4kL/document \
-H "Authorization: Bearer sk_live_abc123" \
-o signed_consulting_agreement.pdfGuide: Using Templates
Templates let you create reusable document blueprints with merge fields. Generate personalized documents by filling in variables, then send them for signing.
Step 1: Browse Available Templates
curl https://api.agreements.ai/v1/templates \
-H "Authorization: Bearer sk_live_abc123"{
"data": [
{
"id": "tpl_NdA9Kx",
"title": "Non-Disclosure Agreement",
"category": "Business",
"variables": ["party_a_name", "party_b_name", "effective_date", "duration_months"],
"createdAt": "2026-01-15T08:00:00Z"
},
{
"id": "tpl_Srv3Mx",
"title": "Service Agreement",
"category": "Services",
"variables": ["client_name", "provider_name", "scope", "hourly_rate", "start_date"],
"createdAt": "2026-01-20T12:00:00Z"
}
],
"total": 374,
"page": 1,
"pageSize": 20
}Step 2: Create a Document from a Template
Pass variable values to generate a fully populated document.
curl -X POST https://api.agreements.ai/v1/templates/tpl_NdA9Kx/use \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"variables": {
"party_a_name": "Acme Corporation",
"party_b_name": "Jane Smith",
"effective_date": "February 12, 2026",
"duration_months": "24"
}
}'{
"id": "doc_Pm4nR7",
"title": "Non-Disclosure Agreement",
"templateId": "tpl_NdA9Kx",
"createdAt": "2026-02-12T10:05:00Z",
"status": "draft"
}Step 3: Send for Signing
Use the returned document ID to create an envelope, exactly as shown in the First Document guide.
curl -X POST https://api.agreements.ai/v1/envelopes \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"documentId": "doc_Pm4nR7",
"name": "NDA — Jane Smith",
"signers": [
{ "name": "Jane Smith", "email": "[email protected]", "role": "signer" }
],
"fields": [
{ "type": "signature", "signerEmail": "[email protected]", "page": 1, "x": 72, "y": 700, "width": 200, "height": 50 }
]
}'Guide: Setting Up Webhooks
Webhooks let your application react to signing events in real time — no polling required. You provide a URL, we send HTTP POST requests when events occur.
Step 1: Create a Webhook
curl -X POST https://api.agreements.ai/v1/webhooks \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/agreements",
"events": [
"envelope.sent",
"envelope.completed",
"envelope.voided",
"envelope.declined",
"signer.completed",
"signer.declined"
],
"secret": "whsec_your_webhook_secret_key"
}'{
"id": "wh_Kj9mN2",
"url": "https://your-app.com/webhooks/agreements",
"events": ["envelope.sent", "envelope.completed", "envelope.voided", "envelope.declined", "signer.completed", "signer.declined"],
"active": true,
"createdAt": "2026-02-12T10:10:00Z"
}Step 2: Handle Events
Your endpoint receives POST requests with a JSON body. Respond with 200 within 30 seconds to acknowledge receipt.
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use('/webhooks/agreements', express.raw({ type: 'application/json' }));
app.post('/webhooks/agreements', (req, res) => {
// 1. Verify signature
const signature = req.headers['x-agreements-signature'];
const expected = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
// 2. Parse and handle event
const event = JSON.parse(req.body);
switch (event.event) {
case 'envelope.completed':
console.log(`Envelope ${event.data.envelopeId} completed!`);
// Update your database, notify your team, trigger next steps...
break;
case 'signer.completed':
console.log(`${event.data.signerName} signed envelope ${event.data.envelopeId}`);
break;
case 'envelope.declined':
console.log(`Envelope ${event.data.envelopeId} declined: ${event.data.reason}`);
// Alert sales team, follow up...
break;
}
res.status(200).json({ received: true });
});
app.listen(3000);Step 3: Verify HMAC Signatures
Every webhook delivery includes a X-Agreements-Signature header containing an HMAC-SHA256 hex digest. Always verify this before processing the payload.
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'utf8'),
Buffer.from(expected, 'utf8')
);
}
// Usage:
const isValid = verifyWebhookSignature(req.body, req.headers['x-agreements-signature'], process.env.WEBHOOK_SECRET);
if (!isValid) return res.status(401).send('Invalid signature');Security: Always verify signatures
timingSafeEqual / compare_digest) to prevent timing attacks.Retry Behavior & Best Practices
- Retries: Failed deliveries (non-2xx response or timeout) are retried up to 5 times with exponential backoff: 1 min, 5 min, 30 min, 2 hours, 24 hours.
- Timeout: Your endpoint must respond within 30 seconds or the delivery is marked as failed.
- Idempotency: Each delivery includes a unique
X-Delivery-Idheader. Store processed IDs to handle duplicate deliveries. - Order: Events are delivered in approximately chronological order but are not guaranteed. Design your handler to be order-independent.
- Respond fast: Return
200immediately, then process asynchronously. Don't do heavy work in the request handler.
Guide: Managing Recipients
Update Signers on a Live Envelope
Correct a signer's email or add new recipients after sending. Only pending signers can be modified — completed signatures are protected.
curl -X PUT https://api.agreements.ai/v1/envelopes/env_Qr8sT4kL/recipients \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"signers": [
{
"id": "sig_A1b2C3",
"name": "Jane Smith-Williams",
"email": "[email protected]"
},
{
"name": "Carol Davis",
"email": "[email protected]",
"role": "cc"
}
]
}'Send Reminders
Nudge pending signers with a reminder email. Rate-limited to 5 per minute.
curl -X POST https://api.agreements.ai/v1/envelopes/env_Qr8sT4kL/remind \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"message": "Friendly reminder: this agreement is awaiting your signature.",
"signerIds": ["sig_D4e5F6"]
}'{
"reminded": ["sig_D4e5F6"],
"skipped": [],
"message": "Reminder sent to 1 signer"
}Void and Resend
If you need to make changes to the document itself, void the envelope and create a new one.
# Void the existing envelope
curl -X POST https://api.agreements.ai/v1/envelopes/env_Qr8sT4kL/void \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{ "reason": "Document updated with revised payment terms" }'
# Create a new envelope with the updated document
curl -X POST https://api.agreements.ai/v1/envelopes \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{ "documentId": "doc_Updated1", "name": "Revised Agreement", ... }'Guide: Migrating from DocuSign
If you're moving from DocuSign's eSignature API, this guide maps concepts and endpoints to help you migrate quickly.
Concept Mapping
| DocuSign | Agreements.ai | Notes |
|---|---|---|
| Envelope | Envelope | Same concept — a signing transaction |
| Tab | Field | signHereTab → signature field, dateSignedTab → date field |
| Recipient | Signer | Same roles: signer, CC, witness |
| Template | Template | Same concept, simpler API |
| Connect (Webhooks) | Webhooks | Simpler setup, HMAC verification included |
| PowerForm | PowerForm | Self-service signing links |
| Account / User | Account | Simplified — one level, no sub-accounts |
API Endpoint Mapping
| DocuSign Endpoint | Agreements.ai Equivalent |
|---|---|
| POST /envelopes | POST /v1/envelopes |
| GET /envelopes/{id} | GET /v1/envelopes/:id |
| PUT /envelopes/{id}/recipients | PUT /v1/envelopes/:id/recipients |
| GET /envelopes/{id}/documents/{docId} | GET /v1/envelopes/:id/document |
| PUT /envelopes/{id} (void) | POST /v1/envelopes/:id/void |
| POST /envelopes/{id}/recipients/{id}/tabs | Included in envelope creation body |
| GET /templates | GET /v1/templates |
| POST /templates/{id}/createEnvelope | POST /v1/templates/:id/use + POST /v1/envelopes |
| POST /connect/configurations | POST /v1/webhooks |
Key Differences
- ✅ No OAuth dance — Use simple API keys instead of OAuth 2.0 JWT grants
- ✅ Flat API structure — No account/user nesting; all endpoints are top-level
- ✅ Fields in envelope body — No separate tab placement calls; fields are included when creating the envelope
- ✅ Simpler webhooks — One POST to register, HMAC verification built in, no Connect configuration XML
- ✅ Documents as a first-class resource — Create and manage documents independently, then attach to envelopes
- ⚠️ No embedded signing (yet) — Recipients sign via email link; embedded iframe signing is on the roadmap
Migration Checklist
- ☐ Create an Agreements.ai account and generate an API key
- ☐ Replace OAuth token logic with API key header
- ☐ Map DocuSign envelope creation to Agreements.ai format (tabs → fields)
- ☐ Update recipient management calls
- ☐ Replace DocuSign Connect with Agreements.ai webhooks
- ☐ Update status checking logic (DocuSign statuses map 1:1)
- ☐ Update document download endpoints
- ☐ Test end-to-end in sandbox before switching production
- ☐ Update error handling for new error format
- ☐ Monitor webhook deliveries for 48 hours after migration
API Reference: Envelopes
Envelopes are the core signing resource. Create, send, track, and manage signing transactions.
API Reference: Documents
Documents contain the actual contract content. Create them from HTML, upload files, or generate from templates.
API Reference: Templates
Reusable document blueprints with merge variables.
API Reference: Webhooks
Register endpoints to receive real-time notifications for signing events.
API Reference: Account
Webhook Events Reference
All events delivered to your webhook endpoint. Each delivery includes headers: X-Agreements-Signature, X-Delivery-Id, X-Event-Type.
envelope.sentTriggered when an envelope is sent to signers (either from API or UI).
{
"event": "envelope.sent",
"timestamp": "2026-02-12T10:00:01Z",
"data": {
"envelopeId": "env_Qr8sT4kL",
"name": "Service Agreement — Acme Corp",
"status": "sent",
"sentAt": "2026-02-12T10:00:01Z",
"signerCount": 2,
"signers": [
{ "name": "Jane Smith", "email": "[email protected]" },
{ "name": "Bob Johnson", "email": "[email protected]" }
]
}
}envelope.completedTriggered when all signers have signed and the envelope is finalized.
{
"event": "envelope.completed",
"timestamp": "2026-02-12T14:30:00Z",
"data": {
"envelopeId": "env_Qr8sT4kL",
"name": "Service Agreement — Acme Corp",
"status": "completed",
"completedAt": "2026-02-12T14:30:00Z",
"documentHash": "sha256:a1b2c3d4e5f6...",
"signers": [
{ "name": "Jane Smith", "email": "[email protected]", "signedAt": "2026-02-12T13:15:00Z" },
{ "name": "Bob Johnson", "email": "[email protected]", "signedAt": "2026-02-12T14:29:45Z" }
]
}
}envelope.voidedTriggered when the sender voids (cancels) an envelope.
{
"event": "envelope.voided",
"timestamp": "2026-02-12T11:00:00Z",
"data": {
"envelopeId": "env_Qr8sT4kL",
"name": "Service Agreement — Acme Corp",
"status": "voided",
"voidedAt": "2026-02-12T11:00:00Z",
"voidReason": "Document updated with revised terms",
"voidedBy": "[email protected]"
}
}envelope.declinedTriggered when a signer declines to sign the envelope.
{
"event": "envelope.declined",
"timestamp": "2026-02-12T12:00:00Z",
"data": {
"envelopeId": "env_Qr8sT4kL",
"name": "Service Agreement — Acme Corp",
"status": "declined",
"declinedBy": { "name": "Jane Smith", "email": "[email protected]" },
"reason": "Payment terms are not acceptable",
"declinedAt": "2026-02-12T12:00:00Z"
}
}signer.completedTriggered when an individual signer completes all their assigned fields.
{
"event": "signer.completed",
"timestamp": "2026-02-12T13:15:00Z",
"data": {
"envelopeId": "env_Qr8sT4kL",
"signerId": "sig_A1b2C3",
"signerName": "Jane Smith",
"signerEmail": "[email protected]",
"signedAt": "2026-02-12T13:15:00Z",
"ipAddress": "203.0.113.42",
"remainingSigners": 1
}
}signer.declinedTriggered when an individual signer declines to sign.
{
"event": "signer.declined",
"timestamp": "2026-02-12T12:00:00Z",
"data": {
"envelopeId": "env_Qr8sT4kL",
"signerId": "sig_A1b2C3",
"signerName": "Jane Smith",
"signerEmail": "[email protected]",
"reason": "Payment terms are not acceptable",
"declinedAt": "2026-02-12T12:00:00Z"
}
}Error Reference
Error Response Format
All errors return a consistent JSON structure:
{
"error": "Human-readable error message",
"code": "MACHINE_READABLE_CODE"
}HTTP Status Codes
| Status | Meaning | When |
|---|---|---|
| 200 | OK | Successful GET, PUT, or DELETE request |
| 201 | Created | Successful POST that creates a resource |
| 400 | Bad Request | Missing required fields, invalid values, malformed JSON |
| 401 | Unauthorized | Missing, invalid, or expired API key / token |
| 403 | Forbidden | Valid key but insufficient permissions for this action |
| 404 | Not Found | Resource does not exist or you don't have access |
| 409 | Conflict | Action conflicts with current state (e.g., voiding a completed envelope) |
| 413 | Payload Too Large | File upload exceeds 25MB limit |
| 429 | Too Many Requests | Rate limit exceeded — check X-RateLimit-Reset header |
| 500 | Internal Server Error | Unexpected server error — contact support if persistent |
Error Codes
| Code | HTTP | Description |
|---|---|---|
| AUTH_INVALID_KEY | 401 | API key is invalid, revoked, or malformed |
| AUTH_EXPIRED_TOKEN | 401 | Firebase JWT token has expired |
| AUTH_MISSING | 401 | No Authorization header provided |
| RATE_LIMIT_EXCEEDED | 429 | Too many requests — wait and retry |
| VALIDATION_ERROR | 400 | Request body failed validation — check the error message for details |
| MISSING_REQUIRED_FIELD | 400 | A required field is missing from the request body |
| INVALID_DOCUMENT_ID | 400 | The documentId does not reference a valid document |
| RESOURCE_NOT_FOUND | 404 | The requested envelope, document, template, or webhook does not exist |
| ENVELOPE_NOT_DRAFT | 409 | Cannot delete/modify — envelope is not in draft status |
| ENVELOPE_ALREADY_COMPLETED | 409 | Cannot void or modify a completed envelope |
| ENVELOPE_ALREADY_VOIDED | 409 | Envelope has already been voided |
| SIGNER_ALREADY_COMPLETED | 409 | Cannot modify a signer who has already signed |
| DOCUMENT_IN_USE | 409 | Cannot delete/modify a document attached to an active envelope |
| WEBHOOK_URL_DUPLICATE | 409 | A webhook already exists for this URL |
| TEMPLATE_MISSING_VARIABLES | 400 | Required template variables were not provided |
| FILE_TOO_LARGE | 413 | Uploaded file exceeds the 25MB size limit |
| INTERNAL_ERROR | 500 | An unexpected error occurred — contact support |
Troubleshooting Common Errors
Getting 401 on every request?
- Verify the header format:
Authorization: Bearer sk_live_...(note the space after "Bearer") - Check that the key hasn't been revoked in Settings → Developers
- Ensure you're using the full key, not a truncated version
Getting 429 Too Many Requests?
- Check
X-RateLimit-Resetheader for when to retry - Implement exponential backoff in your client
- Batch operations where possible instead of individual calls
Getting 409 Conflict?
- You're trying to modify a resource in an incompatible state
- Check the envelope/document status before attempting modifications
- Use
GETto read current state, then decide your next action
Webhook not receiving events?
- Verify the webhook URL is publicly accessible (not localhost)
- Check that the webhook is active:
GET /v1/webhooks/:id - Review recent delivery history for failed attempts
- Use
POST /v1/webhooks/:id/testto send a test event - Ensure your endpoint responds with 200 within 30 seconds
SDKs & Libraries
🚧 Official SDKs Coming Soon
We're building official client libraries for Node.js, Python, Ruby, and Go. In the meantime, the REST API works with any HTTP client in any language.
Node.js / TypeScript
In development
Python
In development
Ruby
Planned
Go
Planned
Community Contributions
We welcome community-built SDKs, wrappers, and example integrations. If you've built something, let us know and we'll feature it here.
🔗 github.com/agreements-aiQuick Reference: Making Requests
No SDK needed — here's the pattern for any language:
curl -X POST https://api.agreements.ai/v1/envelopes \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{"documentId":"doc_123","name":"Agreement","signers":[{"name":"Jane","email":"[email protected]","role":"signer"}]}'Model Context Protocol (MCP)
The Model Context Protocol (MCP) lets AI assistants — like Claude, ChatGPT, and Cursor — interact with Agreements.ai directly. Create documents, analyze contracts, send for signing, and manage negotiations through natural language.
How It Works
MCP is an open standard that connects AI assistants to external tools. Agreements.ai exposes 36 tools via a single endpoint using Streamable HTTP transport (JSON-RPC 2.0).
Endpoint: POST https://api.agreements.ai/v2/mcp
Authentication: Same Authorization: Bearer sk_... header as REST API
Transport: Streamable HTTP (JSON-RPC 2.0)
Content-Type: application/json
When to use MCP vs REST API
MCP: Connection Setup
Claude Desktop
Add this to your Claude Desktop config file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"agreements": {
"command": "npx",
"args": [
"mcp-remote",
"https://api.agreements.ai/v2/mcp",
"--header",
"Authorization: Bearer sk_live_your_api_key_here"
]
}
}
}Cursor IDE
In Cursor Settings → MCP, add a new server:
{
"mcpServers": {
"agreements": {
"command": "npx",
"args": [
"mcp-remote",
"https://api.agreements.ai/v2/mcp",
"--header",
"Authorization: Bearer sk_live_your_api_key_here"
]
}
}
}Raw JSON-RPC Examples
You can also call the MCP endpoint directly using curl:
curl -X POST https://api.agreements.ai/v2/mcp \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-01-01",
"capabilities": {},
"clientInfo": { "name": "my-app", "version": "1.0.0" }
}
}'MCP: Document Tools
MCP: AI Tools
MCP: Signing Tools
MCP: Negotiation Tools
MCP: Templates & Bundles
MCP: Account & Config
Ready to get started?
Create your free account and send your first document for signing in minutes.