Build a code-based Zapier integration for Documenso using @documenso/sdk-typescript,
mirroring the same resources, operations, and patterns as the existing n8n integration
(/Users/lucas/dev/documenso/n8n).
zapier/
├── biome.json
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # App entry (defineApp)
│ ├── authentication.ts # Custom auth (API key + base URL)
│ ├── lib/
│ │ └── client.ts # SDK client factory + error handling
│ ├── triggers/
│ │ └── documentEvent.ts # Webhook trigger (REST hook)
│ ├── creates/
│ │ ├── document/
│ │ │ ├── createDocument.ts
│ │ │ ├── createAndSendDocument.ts
│ │ │ ├── updateDocument.ts
│ │ │ ├── deleteDocument.ts
│ │ │ ├── duplicateDocument.ts
│ │ │ ├── sendDocument.ts
│ │ │ └── resendDocument.ts
│ │ ├── template/
│ │ │ ├── createTemplate.ts
│ │ │ ├── updateTemplate.ts
│ │ │ ├── deleteTemplate.ts
│ │ │ ├── duplicateTemplate.ts
│ │ │ └── useTemplate.ts
│ │ ├── recipient/
│ │ │ ├── createRecipient.ts
│ │ │ ├── updateRecipient.ts
│ │ │ └── deleteRecipient.ts
│ │ ├── field/
│ │ │ ├── createField.ts
│ │ │ ├── updateField.ts
│ │ │ └── deleteField.ts
│ │ ├── folder/
│ │ │ ├── createFolder.ts
│ │ │ ├── updateFolder.ts
│ │ │ └── deleteFolder.ts
│ │ └── attachment/
│ │ ├── createAttachment.ts
│ │ ├── updateAttachment.ts
│ │ └── deleteAttachment.ts
│ └── searches/
│ ├── document/
│ │ ├── findDocument.ts
│ │ └── getDocument.ts
│ ├── template/
│ │ ├── findTemplate.ts
│ │ └── getTemplate.ts
│ ├── recipient/
│ │ └── getRecipient.ts
│ ├── field/
│ │ └── getField.ts
│ ├── folder/
│ │ └── findFolder.ts
│ └── attachment/
│ └── findAttachment.ts
│ └── test/
│ └── authentication.test.ts
In Zapier, there are three action types:
- Triggers - watch for new data (polling or webhook-based)
- Creates - create/modify/delete data in the app
- Searches - find existing data in the app
| n8n Resource | n8n Operation | Zapier Type | Zapier Key |
|---|---|---|---|
| Document | find | Search | document_find |
| Document | get | Search | document_get |
| Document | create | Create | document_create |
| Document | createAndSend | Create | document_create_and_send |
| Document | update | Create | document_update |
| Document | delete | Create | document_delete |
| Document | duplicate | Create | document_duplicate |
| Document | send | Create | document_send |
| Document | resend | Create | document_resend |
| Document | download | -- | Skip for now (binary) |
| Template | find | Search | template_find |
| Template | get | Search | template_get |
| Template | create | Create | template_create |
| Template | update | Create | template_update |
| Template | delete | Create | template_delete |
| Template | duplicate | Create | template_duplicate |
| Template | use | Create | template_use |
| Recipient | create | Create | recipient_create |
| Recipient | get | Search | recipient_get |
| Recipient | update | Create | recipient_update |
| Recipient | delete | Create | recipient_delete |
| Field | create | Create | field_create |
| Field | get | Search | field_get |
| Field | update | Create | field_update |
| Field | delete | Create | field_delete |
| Folder | find | Search | folder_find |
| Folder | create | Create | folder_create |
| Folder | update | Create | folder_update |
| Folder | delete | Create | folder_delete |
| Attachment | find | Search | attachment_find |
| Attachment | create | Create | attachment_create |
| Attachment | update | Create | attachment_update |
| Attachment | delete | Create | attachment_delete |
| Trigger | webhook | Trigger | document_event |
Note: File upload/download operations are skipped initially since Zapier handles
binary data differently (via z.dehydrateFile / z.stashFile). These can be added later.
- Type:
custom - Fields:
apiKey(string, required) - Documenso API keybaseUrl(string, optional, default:https://app.documenso.com/api/v2) - for self-hosted
- Test: Use
client.envelope.envelopeFind({ page: 1, perPage: 1, type: "DOCUMENT" })to verify credentials - Connection label: Show the base URL to distinguish accounts
Mirror the n8n GenericFunctions.ts pattern:
import { Documenso } from "@documenso/sdk-typescript";
export function getDocumensoClient(apiKey: string, baseUrl?: string): Documenso {
return new Documenso({
apiKey,
serverURL: baseUrl || "https://app.documenso.com/api/v2",
});
}Error handling helper that converts SDK errors into Zapier-friendly errors.
- Type: REST Hook (webhook-based)
- Events: DOCUMENT_CREATED, DOCUMENT_SENT, DOCUMENT_OPENED, DOCUMENT_SIGNED, DOCUMENT_COMPLETED, DOCUMENT_REJECTED, DOCUMENT_CANCELLED
- Zapier REST hooks use
subscribeHook/unsubscribeHookfor auto-registration (unlike the n8n manual approach) - Since Documenso doesn't have a webhook management API, this will be a polling trigger or a static webhook (user provides webhook URL manually)
- Decision: Use static webhook (
type: "hook"withperformSubscribe/performUnsubscribeas no-ops, and instructions for users to set up the webhook URL in Documenso)
Each create follows this pattern:
import { defineCreate } from "zapier-platform-core";
export default defineCreate({
key: "document_create",
noun: "Document",
display: {
label: "Create Document",
description: "Creates a new document in Documenso.",
},
operation: {
inputFields: [...],
perform: async (z, bundle) => {
const client = getDocumensoClient(bundle.authData.apiKey, bundle.authData.baseUrl);
// SDK call
},
sample: { ... },
},
});Each search follows this pattern:
import { defineSearch } from "zapier-platform-core";
export default defineSearch({
key: "document_find",
noun: "Document",
display: {
label: "Find Document",
description: "Finds a document in Documenso.",
},
operation: {
inputFields: [...],
perform: async (z, bundle) => {
const client = getDocumensoClient(bundle.authData.apiKey, bundle.authData.baseUrl);
// SDK call
},
sample: { ... },
},
});| Operation | SDK Method |
|---|---|
| Envelope Find | client.envelope.envelopeFind({ type, page, perPage }) |
| Envelope Get | client.envelopes.get({ envelopeId }) |
| Envelope Create | client.envelopes.create({ payload, files }) |
| Envelope Update | client.envelopes.update({ envelopeId, data }) |
| Envelope Delete | client.envelopes.delete({ envelopeId }) |
| Envelope Duplicate | client.envelopes.duplicate({ envelopeId }) |
| Envelope Distribute | client.envelopes.distribute({ envelopeId, ...meta }) |
| Envelope Redistribute | client.envelopes.redistribute({ envelopeId, recipients }) |
| Envelope Use | client.envelopes.use({ payload }) |
| Recipients Create | client.envelopes.recipients.createMany({ envelopeId, data }) |
| Recipients Get | client.envelopes.recipients.get({ recipientId }) |
| Recipients Update | client.envelopes.recipients.updateMany({ envelopeId, data }) |
| Recipients Delete | client.envelopes.recipients.delete({ recipientId }) |
| Fields Create | client.envelopes.fields.createMany({ envelopeId, data }) |
| Fields Get | client.envelopes.fields.get({ fieldId }) |
| Fields Update | client.envelopes.fields.updateMany({ envelopeId, data }) |
| Fields Delete | client.envelopes.fields.delete({ fieldId }) |
| Folders Find | client.folders.find({ page, perPage, query }) |
| Folders Create | client.folders.create({ name, parentId }) |
| Folders Update | client.folders.update({ folderId, data }) |
| Folders Delete | client.folders.delete({ folderId }) |
| Attachments Find | client.envelopes.attachments.find({ envelopeId }) |
| Attachments Create | client.envelopes.attachments.create({ envelopeId, data }) |
| Attachments Update | client.envelopes.attachments.update({ id, data }) |
| Attachments Delete | client.envelopes.attachments.delete({ id }) |
Copy the same biome config as the n8n integration:
- Biome schema 2.3.14
- 2-space indent, double quotes
- Recommended rules +
noExplicitAny: off,useBlockStatements: error - Auto import organization
- Exclude
dist/
- Setup: biome.json, update package.json (name, scripts), update tsconfig.json
- Core:
src/lib/client.ts(SDK factory + error handling) - Auth:
src/authentication.ts(API key + base URL + test) - Trigger:
src/triggers/documentEvent.ts(webhook trigger) - Document creates: create, createAndSend, update, delete, duplicate, send, resend
- Document searches: find, get
- Template creates: create, update, delete, duplicate, use
- Template searches: find, get
- Recipient: create, update, delete (creates) + get (search)
- Field: create, update, delete (creates) + get (search)
- Folder: create, update, delete (creates) + find (search)
- Attachment: create, update, delete (creates) + find (search)
- Index: Wire everything into
src/index.ts - Lint/format: Run biome check
- Build: Verify
tsccompiles cleanly