SignTulip API
通过 REST API 和 Webhook 将电子签名集成到您的产品中。
认证
在「设置 → API 与 Webhook」中创建密钥,并以 Bearer 令牌形式放在 Authorization 请求头中。
curl https://signtulip.com/api/v1/templates \
-H "Authorization: Bearer st_live_..."请求限制:每个 API 密钥每分钟 120 次
接口列表
GET
/api/v1/templatesList templates (yours and your team's).POST
/api/v1/documentsCreate a document from a template and send it.GET
/api/v1/documentsList documents. Query: status, limit (≤100), offset.GET
/api/v1/documents/:idDocument status, recipients and signing links.GET
/api/v1/documents/:id/downloadRedirect to the signed PDF (?type=original for the original).POST
/api/v1/documents/:id/voidVoid a pending document. Body: { reason }.curl -X POST https://signtulip.com/api/v1/documents \
-H "Authorization: Bearer st_live_..." \
-H "Content-Type: application/json" \
-d '{
"template_id": "5b0c…",
"title": "NDA — Acme Inc.",
"locale": "en",
"expires_in_days": 14,
"recipients": [
{ "role": "Signer 1", "name": "Jane Doe", "email": "jane@acme.com" }
]
}'// 201 Created
{
"data": {
"id": "8f3e…",
"title": "NDA — Acme Inc.",
"status": "pending",
"recipients": [
{ "name": "Jane Doe", "email": "jane@acme.com", "status": "sent",
"signing_url": "https://signtulip.com/sign/…" }
]
}
}Webhook
事件发生时,我们会向您的地址 POST 一个 JSON。请使用 SignTulip-Signature 请求头验证请求。
事件类型
document.sentrecipient.viewedrecipient.signedrecipient.declineddocument.completeddocument.voideddocument.expired{
"id": "evt_…",
"type": "document.completed",
"created_at": "2026-09-27T09:00:00.000Z",
"data": { "document": { "id": "8f3e…", "status": "completed", "recipients": [ … ] } }
}验证签名
// SignTulip-Signature: t=1695804000,v1=5f2b…
import crypto from "node:crypto";
function verify(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}错误响应
错误格式为 { "error": { "code", "message" } },并附带相应的 HTTP 状态码。
{ "error": { "code": "template_not_found", "message": "Template not found." } }