HydraSign API
Send documents for signature from your own software, get notified the moment they're signed, and download the signed PDF. Available on the Pro and Business plans.
Authentication
Create an API key in HydraSign under Account → Integrations & API. Send it as a bearer token on every request. The base URL is https://your-domain/api/v1.
curl https://your-domain/api/v1/me -H "Authorization: Bearer hs_live_..."
Requests act on behalf of the company that owns the key. Keep keys on your server — never put them in a website or mobile app.
Send an envelope
POST/envelopes creates the envelope and, unless you pass "send": false, emails/texts the first signer right away.
{
"title": "Work Authorization — Job T-1001",
"message": "Please sign so we can start.",
"routing": "sequential", // or "parallel"
"metadata": { "job_id": "T-1001" }, // your IDs; returned in every webhook
"documents": [ { "name": "authorization.pdf", "file_base64": "JVBERi0xLjcK..." } ],
"recipients": [
{ "ref": "customer", "name": "Jane Customer", "email": "jane@example.com" },
{ "ref": "tech", "name": "Tom Tech", "phone": "206-555-0100", "delivery": "sms" },
{ "name": "Office", "email": "office@example.com", "role": "cc" }
],
"text_tags": [
{ "text": "Customer signature:", "recipient": "customer", "type": "signature" },
{ "text": "Technician signature:", "recipient": "tech", "type": "signature" }
],
"fields": [
{ "recipient": "customer", "type": "date_signed", "page": 1, "x": 190, "y": 372 }
]
}
| Recipient field | Notes |
|---|---|
ref | Your label, used by fields and text_tags (you can also use the recipient's index, 0-based). |
role | signer (default) or cc (gets a copy). |
delivery | email, sms or both. Text messages need the Business plan. |
routing_order | For sequential signing. Defaults to the order you list them. |
access_code | Optional code the signer must enter before viewing. |
Field types: signature, initials, date_signed, full_name, email, company, title, text, checkbox. Positions in fields are in PDF points (1/72 inch) measured from the top-left of the page; width/height are optional. Up to 10 documents, 25 MB each (PDF, JPG or PNG).
The response is the envelope object.
Text tags (automatic field placement)
Instead of coordinates, tell HydraSign what text to look for. It finds every occurrence in the PDF and places the field right after it (or on top of it with "position": "on"). Great for forms your system already generates — print a label like Customer signature:, or a hidden tag like \s1\ in white text.
| Property | Notes |
|---|---|
text | Text to find (case-insensitive unless match_case). |
recipient, type | Who fills it and what kind of field. |
offset_x, offset_y | Nudge in points. Default: 6pt right of the text, sitting on the line. |
width, height, required, label | Optional. |
document | Limit to one document (index). Default: all. |
If a tag isn't found, the request fails with a 400 that names it — nothing is sent.
Templates
Build a template once in HydraSign (documents, roles and fields), then send it with only the people:
GET/templates — lists templates and their role names.
POST /envelopes
{
"template_id": "1f0c...",
"recipients": [
{ "role_name": "Customer", "name": "Ali Hassan", "email": "ali@example.com" },
{ "role_name": "Technician", "name": "Sam Ruiz", "phone": "+12065550100", "delivery": "sms" }
],
"metadata": { "job_id": "T-1002" }
}
Status & signed PDF
GET/envelopes/{id} — the envelope object.
GET/envelopes?status=completed&limit=25 — newest first; page with before=<created_at>. Filter by your IDs with metadata_key=job_id&metadata_value=T-1001.
GET/envelopes/{id}/pdf — the sealed, signed PDF with Certificate of Completion (when completed).
GET/envelopes/{id}/documents/{document_id} — an original document.
POST/envelopes/{id}/remind · POST/envelopes/{id}/void {"reason": "..."} · POST/envelopes/{id}/send (for drafts)
Envelope object
{
"id": "6b1e...", "title": "Work Authorization — Job T-1001", "status": "completed",
"metadata": { "job_id": "T-1001" }, "source": "api",
"created_at": "...", "sent_at": "...", "completed_at": "...", "signed_pdf_sha256": "c50c...",
"documents": [ { "id": "...", "name": "authorization", "pages": 2 } ],
"recipients": [ { "id": "...", "name": "Jane Customer", "email": "jane@example.com", "role": "signer",
"status": "signed", "viewed_at": "...", "signed_at": "..." } ],
"fields": [ { "id": "...", "recipient_id": "...", "type": "text", "label": "Site address", "value": "18500 Main St" } ]
}
Statuses: envelope — draft, sent, completed, declined, voided, expired. Recipient — created (not their turn yet), sent, viewed, signed, declined, completed (CC copy sent).
In-person signing
POST/envelopes/{id}/signing-link {"recipient_id": "..."} returns a fresh signing URL for a signer whose turn it is — open it on your tablet or phone and hand it over. Their previously emailed link stops working.
Webhooks
Add an endpoint under Integrations (or POST/webhooks {"url": "...", "events": []}). HydraSign POSTs JSON to it when something happens:
| Event | When |
|---|---|
envelope.sent | An envelope was sent. |
recipient.sent | A signer was sent their link (it's their turn). |
recipient.viewed | A signer opened the document. |
recipient.signed | A signer finished. |
envelope.completed | Everyone signed — the signed PDF is ready. |
envelope.declined · envelope.voided · envelope.expired | The envelope ended without completing. |
POST https://your-app.com/hydrasign-webhook
HydraSign-Event: envelope.completed
HydraSign-Signature: t=1794677735,v1=5f2c...
{ "id": "evt uuid", "type": "envelope.completed", "created_at": "...",
"data": { "envelope": { ...envelope object... }, "recipient": null } }
Return any 2xx within 10 seconds. Failed deliveries are retried for about two days (1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h). The data shows the envelope as it is when delivered; use id to ignore duplicates.
Verify the signature
// Node.js — use the raw request body
const crypto = require('crypto');
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
Zapier / Make: create a "Webhooks by Zapier → Catch Hook" (or Make "Custom webhook") trigger and paste its URL as a HydraSign webhook. To send documents from a Zap, use a "Webhooks by Zapier → POST" action to /api/v1/envelopes with your API key.
Claude connector
Every API key also works as a Claude connector. In Claude, open Settings → Connectors → Add custom connector, name it HydraSign, and paste https://your-domain/mcp/hs_live_... (shown when you create the key). Then ask things like "who still hasn't signed?", "send the work authorization template to Jane at jane@example.com", or "remind everyone on the Job T-1001 envelope". Tools: account summary, list/get envelopes, list templates, send from template, send reminder, void, in-person signing link.
Errors & limits
| Status | Meaning |
|---|---|
| 400 | Something in the request is wrong — the error message says what. |
| 401 | Missing, invalid or revoked API key. |
| 402 | Your plan doesn't include this (API on Pro/Business; texts on Business), or the trial/subscription has ended. |
| 404 | Not found, or belongs to another company. |
| 429 | More than 300 requests per minute — slow down and retry. |
All errors look like {"error": "message"}.