Skip to main content
API · Reference

Easy Sign API

Automate signature requests and verify documents with a single API call. Perfect for scripts, backend services, and integrations.

Quick Start

  1. 01

    Create an API Key

    Go to Settings → API Keys and create a new key. API access requires an active Pro subscription.

  2. 02

    Prepare Your Document

    Have a PDF ready (URL or base64 encoded). Max 4MB, up to 15 pages.

  3. 03

    Send a Request

    Call the API endpoint to create and send a signature request.

Authentication

API access requires an active Pro subscription. All API requests require an API key passed in the Authorization header:

Authorization: Bearer es_live_your_api_key_here

Rate Limits & Retries

Rate Limit Headers

When you exceed your per-key rate limit, the API returns HTTP 429 with these headers so your client can back off correctly:

HeaderDescription
Retry-AfterSeconds to wait before retrying (rounded up from the reset time)
X-RateLimit-RemainingRequests remaining in the current window (always 0 on a 429)
X-RateLimit-ResetUnix epoch milliseconds when the rate-limit window resets

Successful (2xx) responses currently do not include rate-limit headers — only the 429 response carries them. Plan your retry policy around Retry-After.

Idempotency & Retries

Important. POST /api/agent/sign-request has external side effects: it sends a real email to the recipient. Retrying a failed-but-actually-sent request will create a duplicate envelope and send a second email.

To make retries safe, send an Idempotency-Key header (any unique string, e.g. a UUID, max 255 chars):

-H "Idempotency-Key: 5f3a...-unique-per-attempt"
  • Retrying with the same key replays the original result (same envelope, no second email) and returns Idempotent-Replay: true.
  • Deduplication is keyed on the header only — it does not compare request bodies. A repeated key with a different payload still replays the first result.
  • A concurrent retry while the first is still processing returns 409 IDEMPOTENCY_IN_PROGRESS — wait and check your dashboard before retrying again.
  • For 429, honor Retry-After.

Send Signature Request

POST/api/agent/sign-request

Create and send a signature request in a single call.

Write-only. This API creates and sends; there is no endpoint to read signing status back with an API key. To detect completion, use the Verify API with the transaction_id printed on the completed document, or track envelopes in your dashboard.

Request Example (cURL)

Request
curl -X POST https://www.easy-sign.ca/api/agent/sign-request \
  -H "Authorization: Bearer es_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "recipient": {
      "email": "client@example.com",
      "name": "Jane Smith"
    },
    "document": {
      "url": "https://example.com/contract.pdf"
    },
    "fields": [
      {
        "type": "signature",
        "page": 1,
        "preset": "signature_bottom_right"
      },
      {
        "type": "date",
        "page": 1,
        "preset": "date_after_signature"
      }
    ],
    "envelope_name": "Contract Agreement"
  }'

Request Parameters

The sender identity is derived automatically from the account that owns the API key (your display name and account email). Any sender field in the request body is ignored.

ParameterTypeDescription
recipient.emailstringRecipient's email address (required)
recipient.namestringRecipient's name (optional)
document.urlstringPublic URL to PDF document
document.base64stringBase64 encoded PDF content
document.file_namestringFile name (optional, auto-detected)
fieldsarrayArray of signature fields (required)
envelope_namestringCustom name for the envelope (optional)

Field Configuration

Each field requires a type, page number, and position (using preset or custom coordinates).

Field Types

  • signature - Signature field
  • signature_date - Signature with date
  • date - Date only

Position Presets

  • signature_bottom_right
  • signature_bottom_left
  • signature_bottom_center
  • date_after_signature
  • signature_footer

For custom positioning, use the position object:

{
  "type": "signature",
  "page": 1,
  "position": {
    "x": 60,      // X position (0-100%)
    "y": 85,      // Y position (0-100%)
    "width": 25,  // Width (1-50%)
    "height": 8   // Height (1-20%)
  }
}

Success Response (HTTP 201 Created)

Response
{
  "success": true,
  "envelope_id": "550e8400-e29b-41d4-a716-446655440000",
  "signing_url": "https://www.easy-sign.ca/sign/550e8400...?token=abc123",
  "expires_at": "<ISO 8601 timestamp, ~7 days from request time>",
  "recipient_email": "client@example.com"
}

A successful request returns HTTP 201. If non-critical post-send updates fail (e.g. envelope status flip), the response may include an optional warnings: string[] array — the email has already been sent and cannot be recalled, so treat warnings as advisory.

Error Response

Response
{
  "success": false,
  "error": {
    "code": "INVALID_EMAIL",
    "message": "Valid recipient email is required"
  }
}

Verify Document

POST/api/verify

Verify the authenticity and integrity of signed documents. No authentication required (public API). Rate limited to 20 requests/minute per IP; exceeding it returns HTTP 429 with a Retry-After header.

Verification Types

  • transaction_only - Verify Transaction ID exists in our records
  • full - Upload PDF for complete integrity check (SHA-256 hash comparison)

Request Example (JSON)

Request
curl -X POST https://www.easy-sign.ca/api/verify \
  -H "Content-Type: application/json" \
  -d '{ "transactionId": "ES-20241206-ABC12345" }'

Request Example (with File Verification)

Request
curl -X POST https://www.easy-sign.ca/api/verify \
  -F "file=@signed-document.pdf" \
  -F "transactionId=ES-20241206-ABC12345"

Request Parameters

ParameterTypeDescription
transactionIdstringTransaction ID (format: ES-YYYYMMDD-XXXXXXXX). Found on signed documents.
fileFileOptional: Upload the signed PDF for full integrity verification

Success Response (Full Verification)

Response
{
  "verified": true,
  "transactionId": "ES-20241206-ABC12345",
  "status": "valid",
  "verificationType": "full",
  "message": "Document verified successfully. File integrity confirmed - no modifications detected.",
  "details": {
    "envelopeName": "Contract Agreement",
    "status": "completed",
    "createdAt": "2024-12-06T10:00:00Z",
    "completedAt": "2024-12-06T11:30:00Z",
    "recipientCount": 1,
    "signedCount": 1,
    "documentCount": 1
  }
}

Status Values

StatusDescription
validDocument is fully signed and verified. Integrity check passed (if file was uploaded).
pendingDocument exists but signing is not yet complete.
not_foundTransaction ID does not exist in our records.
tamperedFile has been modified. Hash does not match the original signed document.
voidedDocument was voided by the sender after creation and is no longer legally valid. Returned with verified: false.

Tampered Document Response

Response
{
  "verified": false,
  "transactionId": "ES-20241206-ABC12345",
  "status": "tampered",
  "verificationType": "full",
  "message": "Document has been modified. The uploaded file does not match the original signed document."
}

GET /api/verify (Discovery)

A GET request to the same path returns endpoint metadata — useful for tooling that auto-discovers APIs.

Response
{
  "name": "Easy Sign Document Verification API",
  "version": "1.0",
  "description": "Verify the authenticity of documents signed via Easy Sign",
  "usage": {
    "method": "POST",
    "contentType": "application/json or multipart/form-data",
    "parameters": {
      "transactionId": "The Transaction ID found on the document (format: ES-YYYYMMDD-XXXXXXXX)",
      "file": "Optional: Upload the signed PDF file for verification"
    }
  },
  "legal": {
    "notice": "Documents signed via Easy Sign are legally binding under Canadian provincial e-commerce legislation (e.g., Ontario Electronic Commerce Act, 2000), as well as ESIGN/UETA (US) and eIDAS (EU).",
    "auditTrail": "Complete audit trails are maintained for all signed documents."
  }
}

Integrations & Automation

Wire the API into command-line tools like Claude Code, MCP clients, or your own scripts to send signature requests on your behalf. Ideal for automating document workflows from the tooling you already use.

Claude Code Skill

Save the following as .claude/commands/easy-sign.md in your project or home directory:

.claude/commands/easy-sign.md
---
name: send-signature-request
description: Send a document for electronic signature via Easy Sign API
---

# Send Signature Request

Use this skill when the user wants to send a PDF document for electronic signature.

## Prerequisites
- Easy Sign API key stored in $EASY_SIGN_API_KEY
- PDF document (URL or local file path)

## Usage
Call the Easy Sign API to send a signature request:

```bash
curl -X POST https://www.easy-sign.ca/api/agent/sign-request \
  -H "Authorization: Bearer $EASY_SIGN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "recipient": { "email": "RECIPIENT_EMAIL", "name": "RECIPIENT_NAME" },
    "document": { "url": "DOCUMENT_URL" },
    "fields": [
      { "type": "signature", "page": 1, "preset": "signature_bottom_right" },
      { "type": "date", "page": 1, "preset": "date_after_signature" }
    ],
    "envelope_name": "Document Name"
  }'
```

## Response Handling
On success, return the `signing_url` to the user.
The recipient will receive an email notification.
Link expires in 7 days.

MCP Tool Definition

Use this JSON schema to define an MCP tool for Easy Sign integration:

MCP Tool Schema
{
  "name": "easy_sign_send_signature_request",
  "description": "Send a PDF document for electronic signature. The recipient receives an email with a signing link.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "recipient": {
        "type": "object",
        "properties": {
          "email": { "type": "string", "format": "email" },
          "name":  { "type": "string" }
        },
        "required": ["email"]
      },
      "document": {
        "type": "object",
        "description": "Provide either url or base64 (max 4MB, up to 15 pages)",
        "properties": {
          "url":       { "type": "string", "format": "uri" },
          "base64":    { "type": "string" },
          "file_name": { "type": "string" }
        }
      },
      "fields": {
        "type": "array",
        "minItems": 1,
        "description": "Signature fields. At least one is required. Each field must have either a preset or a position.",
        "items": {
          "type": "object",
          "properties": {
            "type":   { "type": "string", "enum": ["signature", "signature_date", "date"] },
            "page":   { "type": "integer", "minimum": 1 },
            "preset": {
              "type": "string",
              "enum": [
                "signature_bottom_right",
                "signature_bottom_left",
                "signature_bottom_center",
                "signature_footer",
                "date_after_signature"
              ]
            },
            "position": {
              "type": "object",
              "description": "Custom placement as percent of page. Use instead of preset.",
              "properties": {
                "x":      { "type": "number", "minimum": 0, "maximum": 100 },
                "y":      { "type": "number", "minimum": 0, "maximum": 100 },
                "width":  { "type": "number", "minimum": 1, "maximum": 50 },
                "height": { "type": "number", "minimum": 1, "maximum": 20 }
              },
              "required": ["x", "y", "width", "height"]
            },
            "required": { "type": "boolean", "description": "Whether the field is mandatory (default true)" }
          },
          "required": ["type", "page"],
          "anyOf": [
            { "required": ["preset"] },
            { "required": ["position"] }
          ]
        }
      },
      "envelope_name": { "type": "string", "description": "Custom envelope name (optional)" }
    },
    "required": ["recipient", "document", "fields"]
  }
}

Environment Setup

Store your API key securely in an environment variable:

Terminal
# Add to your shell profile (~/.bashrc, ~/.zshrc, etc.)
export EASY_SIGN_API_KEY="es_live_your_api_key_here"

Example Workflow

Here's how an integrated signing flow might look from a tool that calls the API:

User

Send the contract at https://example.com/contract.pdf to john@example.com for signature. I'm jane@company.com.

Assistant

I'll send that document for signature using Easy Sign...

Signature request sent successfully!

John (john@example.com) will receive an email with a link to sign the document. The link expires in 7 days.

Best Practices

  • Confirm document and recipient details before sending
  • Store API keys in environment variables, never hardcode
  • Handle errors gracefully with clear feedback to users
  • Use meaningful envelope names for easy tracking

Error Codes

Sign Request API

CodeHTTPDescription
INVALID_FORMAT401Authorization value present but not a valid key (must start with es_live_)
INVALID_API_KEY401Authorization header missing or malformed, or key not recognized
KEY_REVOKED401API key has been revoked by the owner
KEY_EXPIRED401API key has expired
PRO_REQUIRED401API access requires an active Pro subscription
DATABASE_ERROR401Temporary server error while validating the key — safe to retry
RATE_LIMITED429Too many requests (10/minute per key; contact us to raise)
INVALID_IDEMPOTENCY_KEY400Idempotency-Key exceeds 255 characters
IDEMPOTENCY_IN_PROGRESS409A request with the same Idempotency-Key is still processing
INVALID_JSON400Request body is not valid JSON
INVALID_EMAIL400Email format is invalid
SAME_EMAIL400Sender and recipient are the same
DOCUMENT_TOO_LARGE400Document exceeds the 4 MB (Pro plan) limit
DOCUMENT_TOO_MANY_PAGES400Document exceeds 15 page limit
INVALID_PDF400 / 500Invalid or corrupted PDF (400). Returned as 500 if server-side PDF tooling is temporarily unavailable — safe to retry.
INVALID_URL400Document URL is malformed or unsafe (SSRF protection)
DOCUMENT_URL_FAILED400Failed to download from URL
INVALID_FILE_NAME400File name exceeds 255 characters
NO_FIELDS400No signature fields provided
INVALID_FIELD_TYPE400Field type must be signature, signature_date, or date
INVALID_FIELD_POSITION400Field position is invalid
STORAGE_EXCEEDED400Total storage limit reached for your plan
EMAIL_FAILED500Outbound signature email failed to send (request rolled back)
INTERNAL_ERROR500Unexpected server error — safe to retry after a short delay

Verify API

The Verify API does not use the error.code envelope. Outcomes are conveyed via the status field plus the HTTP status code.

StatusHTTPDescription
valid200Document signed and verified. With a file uploaded, hash integrity is also confirmed.
pending200Document exists but signing is incomplete
tampered200File hash does not match the original signed document
voided200Sender voided the document — no longer legally valid
not_found400Transaction ID format invalid (must be ES-YYYYMMDD-XXXXXXXX)
not_found404Transaction ID does not exist or refers to a deleted record
not_found500Internal server error. The body still carries status: 'not_found' for legacy reasons — treat this as a transient backend failure and retry with jittered exponential backoff.

Limits

4 MB

Max file size (Pro plan)

15

Max pages per document

1

Recipient per request

10/min

Rate limit per API key (contact us to raise)

7 days

Signing link validity

5

API keys per user

Need Help?

Have questions or need assistance? We're here to help.