← FirstInk

API reference

Create files from your case-management, CRM or practice system. The client agreement is pre-filled, signed for the firm (with the firm signature on file, if the agreement needs it) and sent. A webhook tells your system when work can start.

Authentication

Admins create keys in Settings → API. Send Authorization: Bearer ag_live_…. Keys are shown once and stored only as a hash; revoke them any time.

GET /api/v1/templates

Published agreements, with the per-file values each one needs.

{ "templates": [{ "id": "…", "name": "Client Services Agreement", "version": 2,
    "values": [{ "key": "fee", "label": "Fee" }], "has_firm_fields": true }] }

POST /api/v1/files

curl -X POST https://first-ink.vercel.app/api/v1/files \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{
    "template_id": "…",
    "client_name": "Jane Testperson",
    "client_email": "jane@example.test",
    "reference": "2026-0142",        // optional, generated if omitted
    "title": "Estate planning",       // optional
    "values": { "fee": "$2,500" },    // per-file values, if the agreement has any
    "send_email": true                // default true
  }'

201 { "id": "…", "reference": "2026-0142", "status": "awaiting_signature",
      "signing_url": "https://first-ink.vercel.app/sign/…", "email_sent": true, "email_note": null }

422 with error when input is invalid (e.g. a missing value, or the agreement needs a firm signature and none is on file).

GET /api/v1/files/:id

status is the gate: awaiting_signature, ready_to_start, declined or voided. agreement.state shows progress (sent, viewed, consented, completed…).

GET /api/v1/files/:id/document

The signed PDF with its audit certificate. 409 until the agreement is completed.

Webhooks

Add an endpoint in Settings → Webhooks. Events: agreement.completed, agreement.declined, test.ping.

POST <your url>
X-FirstInk-Signature: t=1762718400,v1=<hex>
{ "type": "agreement.completed", "created_at": "…",
  "data": { "file_id": "…", "file_reference": "2026-0142", "agreement_id": "…",
            "client": { "name": "Jane Testperson", "email": "jane@example.test" },
            "completed_at": "…", "sealed_sha256": "…",
            "document_url": "https://first-ink.vercel.app/api/v1/files/…/document" } }

// Verify: v1 == HMAC_SHA256(secret, t + "." + rawBody), and reject old t values.

Each event is attempted once with a 10-second timeout; poll GET /api/v1/files/:id as a fallback.