SignedBySign in

Build on SignedBy. Fast.

A REST API and outbound webhooks for wiring SignedBy into your CRM, app, or onboarding flow — create and send documents, get notified the moment one's signed, and sync it all back automatically. Included in the $29/mo Business plan, not a separate metered developer product like most e-signature APIs.

Start for free →

No credit card required — 3 free documents every month to try it.

$ curl -X POST https://signedby.ai/api/v1/documents \
  -H "Authorization: Bearer sb_live_..." \
  -d '{"template_id":"3c78a1e4-...","signer":{"email":"jane@acme.com"}}'                 

{
  "id": "7fdd90eb-9152-4031-a767-c0632126dc53",
  "status": "sent"
}
The signer's side of the same API call: a handwritten signature drawn in the signature pad on a phone, with a yellow slide-to-sign bar ready to submit

Already works with Pipedrive, HubSpot, Airtable, Notion, Attio, Brevo, and 1,500+ other apps via Make — see the Pipedrive walkthrough below.

Each is a trademark of its respective owner; SignedBy is not affiliated with or endorsed by any of them — they're reachable via Make's own connectors to each, not a native SignedBy integration.

Included in Business ($29/mo)

No separate developer plan or per-seat API tier — see how that compares to DocuSign, SignNow, and PandaDoc on our comparison pages.

REST + webhooks

Plain JSON over HTTPS, HMAC-signed outbound events. No SDK required.

No sandbox needed

The free tier's 3 documents/month is real enough to build and test against.

Rate limit

60 document creates per hour per org — plenty for real usage, generous for testing.

Authentication

Generate a key from Settings → Integration & API (requires the Business plan). Send it as a bearer token on every request:

Authorization: Bearer sb_live_...

Missing or invalid keys get a 401. A key on a plan below Business gets a 402:

401  { "error": "Missing API key. Pass it as 'Authorization: Bearer <key>'." }
401  { "error": "Invalid API key." }
402  { "error": "API access requires the Business plan." }

Endpoints

POST/api/v1/documents

Create a document from a template and send it to one signer.

Request

curl -X POST https://signedby.ai/api/v1/documents \
  -H "Authorization: Bearer sb_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "3c78a1e4-dc56-4769-87f6-65e344dd6d8f",
    "signer": { "email": "jane@acme.com", "name": "Jane", "auth_required": false },
    "expires_at": "2026-08-15T00:00:00Z",
    "invite_subject": "Please sign your Acme agreement",
    "invite_message": "Thanks for your business — just one form to go."
  }'

Response

201
{
  "id": "7fdd90eb-9152-4031-a767-c0632126dc53",
  "status": "sent",
  "expires_at": "2026-08-15T00:00:00Z",
  "auth_required": false
}
POST/api/v1/documents

Multi-party version — send the same template to 2+ role-tagged signers in one call. 'role' maps to the template's Party 1 / Party 2 / … field assignments.

Request

curl -X POST https://signedby.ai/api/v1/documents \
  -H "Authorization: Bearer sb_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "3c78a1e4-dc56-4769-87f6-65e344dd6d8f",
    "signers": [
      { "role": 0, "email": "buyer@acme.com",  "name": "Buyer",  "auth_required": true  },
      { "role": 1, "email": "seller@acme.com", "name": "Seller", "auth_required": false }
    ]
  }'

Response

201
{
  "id": "643d45b3-...",
  "status": "sent",
  "expires_at": null,
  "signers": [
    { "id": "...", "role": 0, "email": "buyer@acme.com",  "auth_required": true  },
    { "id": "...", "role": 1, "email": "seller@acme.com", "auth_required": false }
  ]
}
GET/api/v1/documents?status=completed&limit=20&offset=0

List/search the org's documents. status is optional (draft, sent, completed, declined, voided); limit defaults to 20, max 100.

Request

curl "https://signedby.ai/api/v1/documents?status=completed&limit=20" \
  -H "Authorization: Bearer sb_live_..."

Response

200
{
  "documents": [
    { "id": "...", "title": "Freelance Agreement", "status": "completed",
      "created_at": "2026-07-28T10:04:00Z", "updated_at": "2026-07-29T09:11:00Z",
      "expires_at": null }
  ],
  "total": 42,
  "limit": 20,
  "offset": 0,
  "has_more": true
}
GET/api/v1/documents/{id}

Get a single document's status and its signers' progress.

Request

curl https://signedby.ai/api/v1/documents/<document-id> \
  -H "Authorization: Bearer sb_live_..."

Response

200
{
  "id": "...",
  "title": "Freelance Agreement",
  "status": "completed",
  "created_at": "2026-07-28T10:04:00Z",
  "updated_at": "2026-07-29T09:11:00Z",
  "expires_at": null,
  "signers": [
    { "email": "jane@acme.com", "name": "Jane", "status": "signed",
      "signed_at": "2026-07-29T09:11:00Z", "auth_required": false, "auth_verified": false }
  ]
}
GET/api/v1/templates

List the org's templates, for populating a dropdown in your own UI or a Make scenario.

Request

curl https://signedby.ai/api/v1/templates \
  -H "Authorization: Bearer sb_live_..."

Response

200
{
  "templates": [
    { "id": "3c78a1e4-...", "name": "Freelance Agreement", "page_count": 3,
      "created_at": "2026-07-01T12:00:00Z" }
  ]
}
GET/api/v1/documents/{id}/signed-file

Download the completed, flattened PDF once every signer has signed. 404 until it's ready.

Request

curl https://signedby.ai/api/v1/documents/<document-id>/signed-file \
  -H "Authorization: Bearer sb_live_..." -o signed.pdf

Response

200  <binary PDF>
404  { "error": "Signed PDF isn't ready yet." }
POST/api/v1/documents/{id}/void

Cancel a document that's out for signature — e.g. 'deal fell through, kill the pending contract.' Only works while status is 'sent'.

Request

curl -X POST https://signedby.ai/api/v1/documents/<document-id>/void \
  -H "Authorization: Bearer sb_live_..."

Response

200  { "success": true }
400  { "error": "Only documents that are out for signature can be voided." }

Webhooks

Register one or more endpoint URLs in Settings → Webhooks (each gets its own signing secret). Every enabled endpoint receives all four document lifecycle events:

  • document.viewed — the signer opened the link for the first time
  • document.signed — one signer completed their part (fires per signer, not just once)
  • document.completed — every signer has finished
  • document.declined — a signer declined to sign

Payload shape — signer is omitted on document.completed, since that event isn't about one recipient:

{
  "event": "document.signed",
  "occurred_at": "2026-07-30T09:02:11.000Z",
  "document_id": "7fdd90eb-9152-4031-a767-c0632126dc53",
  "title": "Freelance Agreement",
  "status": "sent",
  "signer": { "email": "jane@acme.com", "name": "Jane" }
}

Each delivery is signed with your endpoint's own secret via an X-SignedBy-Signature header:

X-SignedBy-Signature: sha256=6f2c9e...

Verify it (Node.js) before trusting the payload:

const crypto = require("crypto");

function verify(rawBody, signatureHeader, secret) {
  const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}

Delivery is fire-and-forget with one retry after a 1-second delay (5-second timeout per attempt) — there's no persistent delivery log or manual-redelivery UI yet, so make your endpoint idempotent and check the document's current status via GET /api/v1/documents/{id} if you need to reconcile a missed event.

Connect via Make

There's no native SignedBy app in Make's marketplace yet — you don't need one for either direction:

SignedBy → Make

Add a Custom Webhook trigger module in Make, copy the URL it gives you, and paste it into Settings → Webhooks. Nothing to build on our side.

Make → SignedBy

Use Make's generic HTTP — Make a request module, with your API key as an Authorization: Bearer header, against any endpoint above.

How-to: Pipedrive, both directions

A worked example using Make to connect a Pipedrive deal to a SignedBy contract, start to finish — adapt the same shape for HubSpot, Airtable, or any other CRM.

1. Deal reaches "Contract sent" → send the contract

In Make: a Pipedrive trigger watching for deals entering your "Contract sent" stage, feeding into an HTTP — Make a request module that calls POST /api/v1/documents with the deal's contact as the signer. The contract is out for signature the moment the deal moves stage — no one has to remember to send it.

2. Contract signed → update the deal

A second Make scenario: a Custom Webhook trigger listening for document.completed, feeding into Pipedrive's Update a Deal (or Create a Note) module — logging that the contract came back signed, or moving the deal to its next stage automatically.

Pipedrive is a trademark of its respective owner; SignedBy is not affiliated with or endorsed by Pipedrive or Make.

FAQ & gotchas

What format does expires_at need?

A full UTC ISO-8601 datetime ending in Z — e.g. 2026-08-15T00:00:00Z. A timezone offset like +02:00 is rejected; convert to UTC first.

How does multi-party role numbering work?

role matches the Party 1 / Party 2 / … assignments already on the template — role 0 is Party 1, role 1 is Party 2, and so on. Every role actually used on the template needs a matching signer, or the request is rejected up front.

Is there a sandbox or test mode?

No — the free tier's 3 documents/month is real enough to build and test integrations against before upgrading.

What happens with auth_required signers?

That signer has to enter a one-time email code before the document opens at all — same per-recipient verification the dashboard offers, free on every plan.

Ready to build?

Sign up free, then upgrade to Business ($29/mo) whenever you're ready for your API key.

Start for free →