Skip to content

Bidsketch/signwell-sdk-node

Repository files navigation

@signwell/node-sdk

TypeScript SDK for the SignWell API.

Installation

npm install @signwell/node-sdk

Requires Node.js 18 or newer.

Quick Start

import { Configuration, DocumentApi, FieldType } from '@signwell/node-sdk';

const documents = new DocumentApi(new Configuration({
  apiKey: process.env.SIGNWELL_API_KEY
}));

const document = await documents.createDocument({
  documentRequest: {
    name: 'NDA',
    test_mode: true,
    files: [{ name: 'nda.pdf', file_url: 'https://example.com/nda.pdf' }],
    recipients: [{ id: '1', name: 'Jane Doe', email: 'jane@example.com' }],
    fields: [[{ x: 20, y: 60, page: 1, type: FieldType.Signature, recipient_id: '1' }]]
  }
});

Namespaces

import { Resources, Models, Errors, Embedded, Webhook } from '@signwell/node-sdk';

const api = new Resources.DocumentApi();
const fieldType: Models.FieldType = Models.FieldType.Signature;

Errors

import { DocumentApi, NotFoundError, RateLimitError, extractRateLimitInfo } from '@signwell/node-sdk';

try {
  await new DocumentApi().getDocument({ id: 'missing' });
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log(error.body);
  } else if (error instanceof RateLimitError) {
    console.log('Try again later', extractRateLimitInfo(error));
  }
}

Use raw methods or response middleware when you need headers from successful responses:

const raw = await new DocumentApi().getDocumentRaw({ id: 'doc_123' });
console.log(extractRateLimitInfo(raw.raw));

Compatibility aliases are additive and should not require migration:

Alias Canonical class Purpose
ForbiddenError PermissionDeniedError HTTP 403 responses
TransportError ApiConnectionError Network failures before a response is available

Required argument validation raises RequiredError.

Documents and Templates

import { DocumentApi, TemplateApi } from '@signwell/node-sdk';

const documents = new DocumentApi();

const page = await documents.listDocuments({
  query: 'status:Completed AND name:NDA',
  limit: 100 // clamped to the API maximum of 50
});

for await (const document of documents.iterateDocuments({ query: 'status:Completed' })) {
  console.log(document.id);
}

await documents.updateDocument({
  id: 'doc_123',
  updateDocumentAndSendRequest: {
    subject: 'Updated subject'
  }
}); // Alias for sendDocument / the Update and Send endpoint.

const templates = new TemplateApi();
await templates.listTemplates({ query: 'name:NDA AND archived:false' });

for await (const template of templates.iterateTemplates({ query: 'name:NDA' })) {
  console.log(template.id);
}

Binary Responses

import { BulkSendApi, DocumentApi, RegionalApi } from '@signwell/node-sdk';

const csv = await new BulkSendApi().getBulkSendCsvTemplate({
  templateIds: ['00000000-0000-0000-0000-000000000000']
}); // Blob

const csvJson = await new BulkSendApi().getBulkSendCsvTemplate({
  templateIds: ['00000000-0000-0000-0000-000000000000'],
  base64: true
}); // BulkSendCsvTemplateResponse

const pdf = await new DocumentApi().getCompletedPdf({ id: 'doc_123' }); // Blob
const pdfUrl = await new DocumentApi().getCompletedPdf({ id: 'doc_123', urlOnly: true }); // CompletedPdfUrlResponse
const pdfStream = (await new DocumentApi().getCompletedPdfRaw({ id: 'doc_123' })).stream();

const certificate = await new RegionalApi().getNom151Certificate({ id: 'doc_123' }); // Blob
const certificateUrl = await new RegionalApi().getNom151Certificate({ id: 'doc_123', urlOnly: true }); // Nom151UrlResponse
const certificateObject = await new RegionalApi().getNom151Certificate({ id: 'doc_123', objectOnly: true }); // Nom151CertificateResponse

Embedded Helpers

import { Embedded, FieldType } from '@signwell/node-sdk';

const document = await Embedded.createSigningDocument({
  name: 'NDA',
  files: [{ name: 'nda.pdf', file_url: 'https://example.com/nda.pdf' }],
  recipients: [{ name: 'Jane Doe', email: 'jane@example.com' }],
  fields: [[{ x: 20, y: 60, page: 1, type: FieldType.Signature }]]
});

const url = Embedded.embeddedSigningUrl(document);
const script = Embedded.signingIframe({ url: url ?? '', events: { completed: 'SignWellHandlers.completed' } });

Embedded signing documents must provide fields for every recipient, set with_signature_page: true, or use text_tags: true. The helper validates that shape before making the API request so invalid fieldless signing documents fail locally instead of returning a 422 response.

Embed helpers only accept HTTPS SignWell URLs by default and reject credentialed URLs, http:, javascript:, and arbitrary hosts before rendering script output. For non-production SignWell environments, pass exact hostnames through allowed_embed_hosts. Redirect URLs must be HTTPS and credential-free; pass allowed_redirect_hosts to restrict redirects to your app host.

Webhooks

import { Webhook } from '@signwell/node-sdk';

const replayStore = Webhook.createMemoryReplayStore();

Webhook.verifyEventOrThrow({
  event: payload.event,
  webhookId: process.env.SIGNWELL_WEBHOOK_ID ?? '',
  toleranceSeconds: 300
});

await Webhook.verifyEventOnceOrThrow({
  event: payload.event,
  webhookId: process.env.SIGNWELL_WEBHOOK_ID ?? '',
  toleranceSeconds: 300,
  replayStore
});

verifyEvent and verifyEventOrThrow remain synchronous and do not store replay state. Use verifyEventOnce or verifyEventOnceOrThrow with an atomic WebhookReplayStore when webhook processing has side effects. The in-memory replay store is intended for local development and single-process examples; production apps should back the store with Redis, a database, or another shared atomic insert.

Build

npm ci
npm run validate

About

Official TypeScript / Node.js SDK for the SignWell eSignature API — create, send, and track signing workflows from any Node server. HIPAA, SOC 2, eIDAS compliant.

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages