API Documentation

Everything you need to integrate YesSigned into your application.

Overview

The YesSigned API allows you to programmatically create and manage agreements, templates, webhooks, and API tokens. All API endpoints return JSON responses and use standard HTTP status codes.

Base URL
https://api.yessigned.com/api/v1

All requests must include the header:

Content-Type: application/json
Accept: application/json

Authentication

The API uses Bearer token authentication via Laravel Sanctum. Include your API token in the Authorization header of every request.

Header
Authorization: Bearer YOUR_API_TOKEN

You can generate API tokens from the Tokens API or through the Telegram bot. Tokens are scoped to your account and inherit your plan's rate limits.

If the token is missing or invalid, the API returns:

401 Unauthorized
{
    "message": "Unauthenticated."
}

Rate Limits

API requests are rate-limited to protect the platform. The default limit is 60 requests per minute per token.

Rate limit headers are included in every response:

Response Headers
X-RateLimit-Limit: 60        // Your per-minute limit
X-RateLimit-Remaining: 58    // Requests remaining in current window

When the rate limit is exceeded, the API returns:

429 Too Many Requests
{
    "message": "Too Many Attempts.",
    "retry_after": 30
}

Agreements

Create, retrieve, send, and cancel agreements programmatically.

Endpoints

GET /api/v1/agreements

Returns a paginated list of your agreements.

Parameters
pagePage number (default: 1)
per_pageItems per page (default: 20, max: 100)
statusFilter by status: draft, pending, confirmed, disputed, expired
GET /api/v1/agreements/{id}

Returns a single agreement with participants and confirmations.

POST /api/v1/agreements

Creates a new agreement in draft status.

Parameters
titleAgreement title (required, max 255 chars)
bodyFull agreement text (required)
deadlineISO 8601 deadline date (optional)
template_idTemplate ID to use (optional)
Request
{
    "title": "NDA Agreement",
    "body": "Both parties agree to...",
    "deadline": "2025-12-31T23:59:59Z"
}
POST /api/v1/agreements/{id}/send

Sends a draft agreement to participants. Changes status to pending.

POST /api/v1/agreements/{id}/cancel

Cancels a pending agreement. Only the creator can cancel.

Templates

Manage reusable agreement templates for your team.

Endpoints

GET /api/v1/templates

Returns all templates for your account.

GET /api/v1/templates/{id}

Returns a single template by ID.

POST /api/v1/templates

Creates a new agreement template.

Parameters
nameTemplate name (required, max 255 chars)
bodyTemplate body with placeholders (required)
PUT /api/v1/templates/{id}

Updates an existing template.

DELETE /api/v1/templates/{id}

Permanently deletes a template.

Webhooks

Register webhook endpoints to receive real-time notifications about agreement events.

Endpoints

GET /api/v1/webhooks

Returns all registered webhook endpoints.

GET /api/v1/webhooks/{id}

Returns a single webhook endpoint with delivery history.

POST /api/v1/webhooks

Registers a new webhook endpoint.

Parameters
urlHTTPS endpoint URL (required)
eventsArray of event types to subscribe to (required)
secretSigning secret for HMAC verification (optional, auto-generated if omitted)
Request
{
    "url": "https://example.com/webhooks/yessigned",
    "events": [
        "agreement.created",
        "agreement.confirmed"
    ]
}
PUT /api/v1/webhooks/{id}

Updates a webhook endpoint configuration.

DELETE /api/v1/webhooks/{id}

Deletes a webhook endpoint. Pending deliveries will be cancelled.

API Tokens

Manage API tokens for authentication. Each token is scoped to your account.

Endpoints

GET /api/v1/tokens

Returns all active API tokens for your account.

POST /api/v1/tokens

Creates a new API token. The plain-text token is only returned once.

Parameters
nameToken name for identification (required)

Store the token securely — it cannot be retrieved again.

Response
{
    "token": "1|a3kD9f...",
    "name": "my-integration",
    "created_at": "2025-06-15T10:30:00Z"
}
DELETE /api/v1/tokens/{id}

Revokes an API token. It will immediately stop working.

Webhook Events

When subscribed events occur, YesSigned sends a POST request to your webhook URL with a JSON payload.

Event Types

agreement.created Fired when a new agreement is created.
agreement.confirmed Fired when all participants confirm the agreement.
agreement.cancelled Fired when an agreement is cancelled by the creator.
agreement.disputed Fired when a participant opens a dispute.

Payload Format

All webhook payloads follow this structure:

Webhook Payload
{
    "event": "agreement.confirmed",
    "timestamp": "2025-06-15T10:30:00Z",
    "data": {
        "id": 42,
        "title": "NDA Agreement",
        "status": "confirmed",
        "confirmed_at": "2025-06-15T10:30:00Z",
        "participants": [
            { "user_id": 1, "role": "creator" },
            { "user_id": 5, "role": "participant" }
        ]
    }
}

Webhook Signature Verification

Every webhook request includes an X-Signature header containing an HMAC-SHA256 signature. Use this to verify that the request came from YesSigned and wasn't tampered with.

How It Works

YesSigned signs the raw JSON request body using your webhook's secret key with HMAC-SHA256. The resulting hex digest is sent in the X-Signature header.

  1. Read the raw request body (do not parse JSON first)
  2. Compute HMAC-SHA256 of the body using your webhook secret
  3. Compare the computed signature with the X-Signature header

Code Examples

PHP
$payload = file_get_contents('php://input');
$secret  = 'your-webhook-secret';

$expected = hash_hmac('sha256', $payload, $secret);
$signature = $_SERVER['HTTP_X_SIGNATURE'];

if (!hash_equals($expected, $signature)) {
    http_response_code(403);
    exit('Invalid signature');
}

// hash_equals() is timing-safe
$data = json_decode($payload, true);
Python
import hmac, hashlib

payload   = request.get_data()
secret    = b'your-webhook-secret'
signature = request.headers.get('X-Signature')

expected = hmac.new(
    secret, payload, hashlib.sha256
).hexdigest()

if not hmac.compare_digest(expected, signature):
    abort(403)

# hmac.compare_digest() is timing-safe
data = request.get_json()
Node.js
const crypto = require('crypto');

const payload   = req.rawBody;
const secret    = 'your-webhook-secret';
const signature = req.headers['x-signature'];

const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

const valid = crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
);

if (!valid) return res.status(403).send('Invalid signature');

// crypto.timingSafeEqual() prevents timing attacks
const data = JSON.parse(payload);

Always use a timing-safe comparison function to prevent timing attacks. Never use simple string equality (== or ===) for signature comparison.