REST API reference

The Inktivo API lets you automate envelope workflows, integrate signing into your own applications, and receive real-time event notifications via webhooks.

Business plan required. API access is only available on the Business plan. Manage API keys under Account settings → API. Only Owners and Admins can create or revoke keys.

Authentication

All API requests must include a valid API key in the Authorization header as a Bearer token:

Authorization: Bearer sk_live_your_key_here

API keys begin with sk_live_. They are generated in the Inktivo dashboard under Account settings → API. A key is shown only once at creation — store it securely in an environment variable. Treat API keys like passwords; do not commit them to source control.

Keys that are unused for an extended period or that you no longer need should be revoked from the dashboard.

Base URL

All endpoints are relative to:

https://inktivo.com/api/v1

All requests and responses use application/json. CORS is enabled for all origins, so you can call the API directly from browser-based apps.

Errors

Inktivo uses standard HTTP status codes. When an error occurs, the response body contains a single error field with a human-readable message:

{
  "error": "Only DRAFT envelopes can be sent"
}
StatusMeaning
200OK — Request succeeded.
201Created — Resource was created (e.g. webhook).
204No Content — Success, no body (e.g. DELETE).
400Bad Request — Invalid or missing parameters.
401Unauthorized — Missing, invalid, or revoked API key.
404Not Found — The requested resource does not exist or does not belong to your organization.
422Unprocessable — The request was well-formed but the action cannot be performed (e.g. sending an already-sent envelope).

Envelopes

GEThttps://inktivo.com/api/v1/envelopes

Returns a paginated list of envelopes for your organization.

Query parameters

ParameterTypeDescription
pageintegerPage number. Defaults to 1.
per_pageintegerResults per page. Defaults to 20, maximum 100.
statusstringFilter by status: DRAFT, SENT, COMPLETED, VOIDED, or EXPIRED.

Response

{
  "data": [
    {
      "id": "clx1abc2def3",
      "title": "NDA — Acme Corp",
      "status": "SENT",
      "signingOrder": "SEQUENTIAL",
      "createdAt": "2025-06-01T10:00:00.000Z",
      "sentAt": "2025-06-01T10:05:00.000Z",
      "completedAt": null,
      "voidedAt": null,
      "expiresAt": null,
      "documents": [
        { "id": "doc1", "name": "nda.pdf", "pageCount": 4 }
      ],
      "recipients": [
        {
          "id": "rec1",
          "name": "Jane Smith",
          "email": "jane@example.com",
          "role": "SIGNER",
          "status": "SENT",
          "completedAt": null
        }
      ]
    }
  ],
  "pagination": {
    "total": 42,
    "page": 1,
    "perPage": 20,
    "totalPages": 3
  }
}
GEThttps://inktivo.com/api/v1/envelopes/:id

Returns a single envelope with full detail including all recipient timestamps.

Response

{
  "data": {
    "id": "clx1abc2def3",
    "title": "NDA — Acme Corp",
    "status": "COMPLETED",
    "signingOrder": "SEQUENTIAL",
    "createdAt": "2025-06-01T10:00:00.000Z",
    "sentAt": "2025-06-01T10:05:00.000Z",
    "completedAt": "2025-06-01T14:22:00.000Z",
    "voidedAt": null,
    "expiresAt": null,
    "documents": [
      { "id": "doc1", "name": "nda.pdf", "pageCount": 4, "order": 0 }
    ],
    "recipients": [
      {
        "id": "rec1",
        "name": "Jane Smith",
        "email": "jane@example.com",
        "role": "SIGNER",
        "order": 1,
        "status": "COMPLETED",
        "sentAt": "2025-06-01T10:05:00.000Z",
        "viewedAt": "2025-06-01T13:10:00.000Z",
        "completedAt": "2025-06-01T14:22:00.000Z",
        "declinedAt": null
      }
    ]
  }
}
POSThttps://inktivo.com/api/v1/envelopes/:id/send

Sends a DRAFT envelope for signing. Signing-invitation emails are delivered to the appropriate recipients immediately.

No request body is required.

Preconditions

  • The envelope must be in DRAFT status.
  • The envelope must have at least one recipient.
  • At least one field must be placed on the documents.

Response (200)

{
  "data": {
    "id": "clx1abc2def3",
    "status": "SENT"
  }
}
POSThttps://inktivo.com/api/v1/envelopes/:id/void

Voids an envelope. All recipient signing links become invalid immediately. Cannot be undone.

Request body

ParameterTypeDescription
reasonstringOptional reason for voiding (e.g. 'Terms changed'). Recorded in the audit trail.
{
  "reason": "Contract terms updated — new version incoming"
}

Preconditions

  • The envelope must not already be COMPLETED or VOIDED.

Response (200)

{
  "data": {
    "id": "clx1abc2def3",
    "status": "VOIDED"
  }
}

Webhooks

Webhooks let Inktivo push event notifications to your server in real time. When a relevant event occurs (e.g. a recipient signs), Inktivo sends an HTTP POST request to your endpoint with a JSON payload.

GEThttps://inktivo.com/api/v1/webhooks

Returns all webhook endpoints for your organization.

Response

{
  "data": [
    {
      "id": "wh1abc",
      "url": "https://myapp.example.com/webhooks/inktivo",
      "events": ["envelope.completed", "recipient.signed"],
      "enabled": true,
      "createdAt": "2025-06-01T10:00:00.000Z"
    }
  ]
}
POSThttps://inktivo.com/api/v1/webhooks

Registers a new webhook endpoint.

Request body

ParameterTypeDescription
urlrequiredstringThe HTTPS URL Inktivo will POST events to. Must start with https://.
eventsrequiredstring[]One or more event types to subscribe to (see Event types below).
{
  "url": "https://myapp.example.com/webhooks/inktivo",
  "events": ["envelope.completed", "envelope.voided", "recipient.signed"]
}

Response (201)

Save the secret. The secret field is returned only once. Store it securely — you will use it to verify webhook signatures.
{
  "data": {
    "id": "wh1abc",
    "url": "https://myapp.example.com/webhooks/inktivo",
    "events": ["envelope.completed", "recipient.signed"],
    "enabled": true,
    "createdAt": "2025-06-01T10:00:00.000Z",
    "secret": "a3f9b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1"
  }
}
DELETEhttps://inktivo.com/api/v1/webhooks/:id

Permanently deletes a webhook endpoint. No more events will be delivered to it.

Returns 204 No Content on success.

Webhook event types

Pass one or more of the following strings in the events array:

EventTriggered when
envelope.sentAn envelope is sent and signing invitations are dispatched.
envelope.completedAll required signers have completed the envelope.
envelope.voidedAn envelope is voided by a team member or via the API.
recipient.signedA single recipient completes their required fields.
recipient.declinedA recipient declines to sign and provides a reason.

Payload format

Every webhook POST has Content-Type: application/json and a body of the form:

// envelope.completed example
{
  "event": "envelope.completed",
  "data": {
    "envelopeId": "clx1abc2def3",
    "title": "NDA — Acme Corp"
  },
  "timestamp": "2025-06-01T14:22:00.000Z"
}

The data object for each event:

Eventdata fields
envelope.sentenvelopeId, title
envelope.completedenvelopeId, title
envelope.voidedenvelopeId, title, reason (may be empty)
recipient.signedenvelopeId, recipientId, recipientName, recipientEmail
recipient.declinedenvelopeId, recipientId, recipientName, recipientEmail, reason

Verifying webhook signatures

Inktivo signs every webhook request with HMAC-SHA256 using the secret returned when you created the endpoint. Two headers are included:

  • X-Inktivo-Signaturesha256=<hex digest>
  • X-Inktivo-Event — the event type string (e.g. envelope.completed)

To verify, compute HMAC-SHA256(secret, rawBody) and compare it to the hex digest in the X-Inktivo-Signature header (after stripping the sha256= prefix). Always use a constant-time comparison to prevent timing attacks.

Node.js verification example

import crypto from "crypto";

export function verifyInktivoWebhook(
  secret,      // the secret you saved when creating the webhook
  rawBody,     // the raw request body string (before JSON.parse)
  signature,   // the X-Inktivo-Signature header value
) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  const received = signature.replace(/^sha256=/, "");

  return crypto.timingSafeEqual(
    Buffer.from(expected, "hex"),
    Buffer.from(received, "hex"),
  );
}

// Express handler example
app.post("/webhooks/inktivo", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["x-inktivo-signature"];
  const event = req.headers["x-inktivo-event"];

  if (!verifyInktivoWebhook(process.env.INKTIVO_WEBHOOK_SECRET, req.body.toString(), sig)) {
    return res.status(401).send("Invalid signature");
  }

  const payload = JSON.parse(req.body.toString());

  if (event === "envelope.completed") {
    console.log("Envelope completed:", payload.data.envelopeId);
    // trigger your downstream workflow here
  }

  res.sendStatus(200);
});

Python (Flask) verification example

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
INKTIVO_SECRET = os.environ["INKTIVO_WEBHOOK_SECRET"]

@app.route("/webhooks/inktivo", methods=["POST"])
def inktivo_webhook():
    signature = request.headers.get("X-Inktivo-Signature", "")
    event = request.headers.get("X-Inktivo-Event", "")
    raw_body = request.get_data()  # must be raw bytes

    expected = hmac.new(
        INKTIVO_SECRET.encode(),
        raw_body,
        hashlib.sha256,
    ).hexdigest()

    received = signature.removeprefix("sha256=")

    if not hmac.compare_digest(expected, received):
        abort(401)

    payload = request.get_json()

    if event == "envelope.completed":
        print(f"Completed: {payload['data']['envelopeId']}")

    return "", 200

Code examples

cURL — list envelopes

curl -s -H "Authorization: Bearer sk_live_your_key_here" \
  "https://inktivo.com/api/v1/envelopes?status=SENT&per_page=5" | jq .

cURL — send an envelope

curl -s -X POST \
  -H "Authorization: Bearer sk_live_your_key_here" \
  "https://inktivo.com/api/v1/envelopes/clx1abc2def3/send" | jq .

Node.js — fetch and process completed envelopes

const API_KEY = process.env.INKTIVO_API_KEY;
const BASE_URL = "https://inktivo.com/api/v1";

async function fetchCompleted(page = 1) {
  const res = await fetch(
    `${BASE_URL}/envelopes?status=COMPLETED&page=${page}&per_page=50`,
    { headers: { Authorization: `Bearer ${API_KEY}` } },
  );

  if (!res.ok) {
    const { error } = await res.json();
    throw new Error(`Inktivo API error: ${error}`);
  }

  const { data, pagination } = await res.json();

  for (const envelope of data) {
    console.log(`${envelope.title} — completed at ${envelope.completedAt}`);
  }

  if (pagination.page < pagination.totalPages) {
    await fetchCompleted(page + 1);
  }
}

fetchCompleted().catch(console.error);

Python — void an envelope

import os, requests

API_KEY = os.environ["INKTIVO_API_KEY"]
BASE_URL = "https://inktivo.com/api/v1"

def void_envelope(envelope_id: str, reason: str = ""):
    response = requests.post(
        f"{BASE_URL}/envelopes/{envelope_id}/void",
        json={"reason": reason},
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    response.raise_for_status()
    return response.json()["data"]

result = void_envelope("clx1abc2def3", reason="Contract terms revised")
print(result)  # {'id': 'clx1abc2def3', 'status': 'VOIDED'}

Node.js — create a webhook

const res = await fetch(`https://inktivo.com/api/v1/webhooks`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.INKTIVO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://myapp.example.com/webhooks/inktivo",
    events: ["envelope.completed", "envelope.voided", "recipient.signed"],
  }),
});

const { data } = await res.json();

// IMPORTANT: save data.secret — it will not be shown again
console.log("Webhook secret:", data.secret);
console.log("Webhook ID:", data.id);