SDKs
TypeScript SDK
@signyt/sdk is a dependency-free, fetch-based client with types generated from the OpenAPI spec. It runs on Node 18+ and other runtimes with fetch, FormData and Web Crypto. Keep API keys on the server.
Install#
npm install @signyt/sdkCreate a client#
import { Signyt } from "@signyt/sdk";
const client = new Signyt({
apiKey: process.env.SIGNYT_API_KEY!,
// baseUrl: "https://api.sign.yt/v1",
// maxRetries: 2, retryBackoffMs: 500, timeoutMs: 30_000, fetch: customFetch,
});
// or simply: new Signyt(process.env.SIGNYT_API_KEY!)| Option | Default | Description |
|---|---|---|
apiKey | required | An sk_live_... key. |
baseUrl | https://api.sign.yt/v1 | API base URL. |
maxRetries | 2 | Retries for GET/PUT/DELETE on 429, 5xx and network errors. |
retryBackoffMs | 500 | Base backoff (exponential, jittered; Retry-After wins, capped at 60 s). |
timeoutMs | 30000 | Per-attempt timeout. |
fetch | globalThis.fetch | Custom fetch implementation. |
Resources#
| Method | Endpoint |
|---|---|
documents.list({ limit, offset }) | GET /documents |
documents.get(id) | GET /documents/{id} |
documents.create({ pdf, title, recipients, signingOrder, verificationType, metadata, expiresAt, allowDelegation, teamId, folderId, filename }) | POST /documents; pdf is a Blob, Uint8Array (including Node Buffer) or ArrayBuffer |
documents.setFields(id, fields) | PUT /documents/{id}/fields |
documents.detectFields(id) | POST /documents/{id}/detect-fields |
documents.updateRecipient(id, recipientId, { email, name, phone }) | PATCH /documents/{id}/recipients/{recipientId} |
documents.resend(id, recipientId) | POST .../recipients/{recipientId}/resend |
documents.send(id), documents.remind(id) | POST /documents/{id}/send, /remind |
documents.cancel(id, reason, copyAsDraft?) | POST /documents/{id}/cancel |
documents.delete(id), documents.restore(id) | DELETE /documents/{id}, POST /documents/{id}/restore |
documents.downloadSignedPdf(id), documents.downloadOriginalPdf(id) | Resolve to Uint8Array |
documents.audit(id) | GET /documents/{id}/audit |
templates.list(), templates.create({ pdf, name, description, category }), templates.use(id, { title, recipients }), templates.delete(id) | /templates |
webhooks.list(), webhooks.create({ url, events }), webhooks.update(id, { url, events, active }), webhooks.delete(id) | /webhooks |
Exported types include DocumentDetail, DocumentSummary, RecipientInput, FieldInput, TemplateRead, WebhookRead, WebhookCreated and the status enums. For endpoints without a method (flows, public links, deliveries, signing links, evidence, cursor pagination) use fetch directly; the guides have examples.
End-to-end example#
import { readFile, writeFile } from "node:fs/promises";
import { Signyt } from "@signyt/sdk";
const client = new Signyt(process.env.SIGNYT_API_KEY!);
const doc = await client.documents.create({
pdf: await readFile("contract.pdf"),
filename: "contract.pdf",
title: "Service agreement",
recipients: [{ email: "[email protected]", name: "Anna Nowak" }],
verificationType: "EMAIL_OTP",
metadata: { crm_deal_id: "D-1042" },
});
await client.documents.setFields(doc.id, [
{ type: "SIGNATURE", page: 1, position_x: 15, position_y: 80, width: 25, height: 8, recipient_id: doc.recipients[0].id },
]);
await client.documents.send(doc.id);
// later, after document.completed:
const done = await client.documents.get(doc.id);
if (done.status === "COMPLETED") await writeFile("contract-signed.pdf", await client.documents.downloadSignedPdf(doc.id));Errors#
Failed requests reject with a subclass of SignytError carrying status, code, message, extra and requestId: BadRequestError (400, 413, 422), AuthenticationError (401), PaymentRequiredError (402), PermissionDeniedError (403), NotFoundError (404), ConflictError (409), RateLimitError (429, with retryAfter), ServerError (5xx) and APIConnectionError (network or timeout).
import { ConflictError, Signyt, SignytError } from "@signyt/sdk";
const client = new Signyt(process.env.SIGNYT_API_KEY!);
try {
await client.documents.remind(process.env.DOC_ID!);
} catch (e) {
if (e instanceof ConflictError && e.code === "document_not_pending") console.log("nothing to remind");
else if (e instanceof SignytError) console.error(e.status, e.code, e.message, e.requestId);
else throw e;
}Verify webhooks#
import { createServer } from "node:http";
import { constructWebhookEvent, SignatureVerificationError } from "@signyt/sdk";
createServer(async (req, res) => {
const chunks: Buffer[] = [];
for await (const c of req) chunks.push(c as Buffer);
const raw = new Uint8Array(Buffer.concat(chunks));
try {
const event = await constructWebhookEvent(
raw,
req.headers as Record<string, string>,
process.env.SIGNYT_WEBHOOK_SECRET!,
);
console.log(event.event, event.data);
res.writeHead(204).end();
} catch (e) {
res.writeHead(e instanceof SignatureVerificationError ? 400 : 500).end();
}
}).listen(3000);constructWebhookEvent checks the HMAC (constant time, via Web Crypto) and a 5-minute timestamp window (tolerance in seconds), then returns { event, timestamp, data }. verifyWebhookSignature(payload, header, secret, timestamp) is the lower-level check. See Webhooks.