v1 — Last updated February 2026

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

Your API key is only displayed once at creation. Store it in an environment variable or secrets manager — never commit it to source control.

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_here

Complete 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.pdf

Authentication

API Keys vs Firebase Tokens

Agreements.ai supports two authentication methods:

MethodFormatUse Case
sk_live_...API KeyServer-to-server integrations, scripts, backend automation
eyJhbG...Firebase JWTFrontend/mobile apps using Firebase Auth (short-lived)

Both use the same header: Authorization: Bearer <token>

Creating and Managing Keys

  1. Go to Settings → Developers
  2. Click Create API Key
  3. Give it a descriptive name (e.g., "CRM Integration", "CI/CD Pipeline")
  4. 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.

OperationLimitWindowEndpoints
Reads30 requestsPer minuteAll GET endpoints
Writes10 requestsPer minutePOST, PUT, PATCH, DELETE
Reminders5 requestsPer minutePOST /envelopes/:id/remind

Rate limit headers are included in every response: X-RateLimit-Remaining, X-RateLimit-Reset

Auth Error Responses

json401 Unauthorized
{
  "error": "Invalid or expired API key",
  "code": "AUTH_INVALID_KEY"
}
json429 Too Many Requests
{
  "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:

DraftSentIn ProgressCompleted

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>"
  }'
jsonResponse
{
  "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
    }
  }'
jsonResponse
{
  "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"
💡
Instead of polling, set up a webhook to get notified instantly when signers complete.

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.pdf

Guide: 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"
jsonResponse
{
  "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"
    }
  }'
jsonResponse
{
  "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"
  }'
jsonResponse
{
  "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

Never process a webhook payload without verifying the HMAC signature. Use constant-time comparison (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-Id header. 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 200 immediately, 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"]
  }'
jsonResponse
{
  "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

DocuSignAgreements.aiNotes
EnvelopeEnvelopeSame concept — a signing transaction
TabFieldsignHereTab → signature field, dateSignedTab → date field
RecipientSignerSame roles: signer, CC, witness
TemplateTemplateSame concept, simpler API
Connect (Webhooks)WebhooksSimpler setup, HMAC verification included
PowerFormPowerFormSelf-service signing links
Account / UserAccountSimplified — one level, no sub-accounts

API Endpoint Mapping

DocuSign EndpointAgreements.ai Equivalent
POST /envelopesPOST /v1/envelopes
GET /envelopes/{id}GET /v1/envelopes/:id
PUT /envelopes/{id}/recipientsPUT /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}/tabsIncluded in envelope creation body
GET /templatesGET /v1/templates
POST /templates/{id}/createEnvelopePOST /v1/templates/:id/use + POST /v1/envelopes
POST /connect/configurationsPOST /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.sent

Triggered when an envelope is sent to signers (either from API or UI).

jsonPayload
{
  "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.completed

Triggered when all signers have signed and the envelope is finalized.

jsonPayload
{
  "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.voided

Triggered when the sender voids (cancels) an envelope.

jsonPayload
{
  "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.declined

Triggered when a signer declines to sign the envelope.

jsonPayload
{
  "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.completed

Triggered when an individual signer completes all their assigned fields.

jsonPayload
{
  "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.declined

Triggered when an individual signer declines to sign.

jsonPayload
{
  "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

StatusMeaningWhen
200OKSuccessful GET, PUT, or DELETE request
201CreatedSuccessful POST that creates a resource
400Bad RequestMissing required fields, invalid values, malformed JSON
401UnauthorizedMissing, invalid, or expired API key / token
403ForbiddenValid key but insufficient permissions for this action
404Not FoundResource does not exist or you don't have access
409ConflictAction conflicts with current state (e.g., voiding a completed envelope)
413Payload Too LargeFile upload exceeds 25MB limit
429Too Many RequestsRate limit exceeded — check X-RateLimit-Reset header
500Internal Server ErrorUnexpected server error — contact support if persistent

Error Codes

CodeHTTPDescription
AUTH_INVALID_KEY401API key is invalid, revoked, or malformed
AUTH_EXPIRED_TOKEN401Firebase JWT token has expired
AUTH_MISSING401No Authorization header provided
RATE_LIMIT_EXCEEDED429Too many requests — wait and retry
VALIDATION_ERROR400Request body failed validation — check the error message for details
MISSING_REQUIRED_FIELD400A required field is missing from the request body
INVALID_DOCUMENT_ID400The documentId does not reference a valid document
RESOURCE_NOT_FOUND404The requested envelope, document, template, or webhook does not exist
ENVELOPE_NOT_DRAFT409Cannot delete/modify — envelope is not in draft status
ENVELOPE_ALREADY_COMPLETED409Cannot void or modify a completed envelope
ENVELOPE_ALREADY_VOIDED409Envelope has already been voided
SIGNER_ALREADY_COMPLETED409Cannot modify a signer who has already signed
DOCUMENT_IN_USE409Cannot delete/modify a document attached to an active envelope
WEBHOOK_URL_DUPLICATE409A webhook already exists for this URL
TEMPLATE_MISSING_VARIABLES400Required template variables were not provided
FILE_TOO_LARGE413Uploaded file exceeds the 25MB size limit
INTERNAL_ERROR500An 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-Reset header 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 GET to 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/test to 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-ai

Quick 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

Use MCP when integrating with AI assistants (Claude Desktop, Cursor, etc.). Use the REST API for programmatic server-to-server integrations. Both use the same API key.

MCP: Connection Setup

Claude Desktop

Add this to your Claude Desktop config file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

jsonClaude Desktop Configuration
{
  "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:

jsonCursor MCP Settings
{
  "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.

© 2026 Agreements.ai. All rights reserved.