API & MCP

Send a document for signature from your own code, or straight from an AI assistant. Included on every plan, free included. No upgrade to try it.

From an AI assistant (MCP)

FairSign is a remote MCP server with OAuth, so connecting is a sign-in, not a pasted secret. Add this URL as a custom connector; your assistant registers itself, sends you here to log in and approve, and gets a token scoped to your account. You can disconnect it any time from /settings/api.

https://www.usefairsign.com/api/mcp

Discovery is where the spec says it should be: /.well-known/oauth-protected-resource (RFC 9728) and /.well-known/oauth-authorization-server (RFC 8414). Authorization code with PKCE (S256 only), dynamic client registration (RFC 7591), and rotating refresh tokens.

Three tools:

  • send_for_signature: email a PDF to people and get a signing link back for each of them.
  • check_status: who signed, who has not, when it completed.
  • list_requests: recent signature requests.

From code, with an API key instead

A script has nobody to click "approve", so it uses a key from /settings/api, sent as Authorization: Bearer fs_live_…. We store only a hash, so it is shown exactly once. The same key also works on the MCP endpoint.

POST /api/v1/envelopes: send for signature. Returns 201 with an id and a signing link per recipient.

curl -X POST https://www.usefairsign.com/api/v1/envelopes \
  -H "Authorization: Bearer fs_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "contract.pdf",
    "fileBase64": "JVBERi0xLjQK...",
    "recipients": [
      { "email": "sam@example.com", "name": "Sam" }
    ],
    "message": "Last page, please."
  }'

GET /api/v1/envelopes/{id}: status. GET /api/v1/envelopes: the list.

Webhooks, so you never have to poll

Add a URL under API keys and we POST a JSON event to it when an envelope completes, a signer signs, or someone declines. Each delivery carries x-fairsign-event, x-fairsign-timestampand x-fairsign-signature: an HMAC-SHA256 of {timestamp}.{body} under the secret shown once when you add the hook. Verify it before trusting the payload. A receiver that answers anything other than 2xx gets one retry; after that the audit trail is still the record.

{
  "event": "envelope.completed",
  "occurredAt": "2026-09-06T14:02:11.000Z",
  "data": { "envelopeId": "…", "sha256": "…", "signers": ["maya@example.com"] }
}

Errors worth handling

  • 401 unauthorized: missing, unknown, or revoked key.
  • 422: bad input; message says which field.
  • 429 quota_exceeded: the free plan’s three requests for the month are gone. 429 rate_limited: the daily fair-use line.

What it deliberately does not do

There is no field-placement parameter. Software calling this cannot see the page, and a signature box landing on top of a paragraph is worse than letting each signer place their own. Signing your own document is also not here. That happens in the browser at /sign and the file never leaves the device, which an API call could not honestly claim.