|
| 1 | +--- |
| 2 | +description: n8n community node for Documenso document signing |
| 3 | +globs: "*.ts, *.json" |
| 4 | +alwaysApply: true |
| 5 | +--- |
| 6 | + |
| 7 | +# @documenso/n8n-nodes-documenso |
| 8 | + |
| 9 | +n8n community node package for the Documenso open source document signing platform. Uses the `@documenso/sdk-typescript` SDK internally, exposing it through n8n's node UI. |
| 10 | + |
| 11 | +## Commands |
| 12 | + |
| 13 | +- `bun install` — install dependencies |
| 14 | +- `bun run build` — build with tsdown (CJS output to dist/) |
| 15 | +- `bun run typecheck` — type-check with tsc --noEmit |
| 16 | +- `bun run check` — biome check --write (lint + format) |
| 17 | +- `bun run lint` — biome lint only |
| 18 | +- `bun run format` — biome format --write only |
| 19 | +- `bun run dev` — docker compose up (runs n8n with the node loaded at http://localhost:5678) |
| 20 | + |
| 21 | +After changes: `bun run build && docker compose restart` to see them in n8n. |
| 22 | + |
| 23 | +## Code Style |
| 24 | + |
| 25 | +- **Formatter**: Biome — 2-space indent, double quotes |
| 26 | +- **Block statements**: Always use braces (`if (x) { ... }`, never `if (x) ...`) |
| 27 | +- **`any` types**: Allowed (biome `noExplicitAny: off`) — unavoidable with n8n's `getNodeParameter` and SDK response types |
| 28 | +- **Imports**: Auto-organized by biome assist |
| 29 | + |
| 30 | +## Architecture |
| 31 | + |
| 32 | +### Source layout (`src/`) |
| 33 | + |
| 34 | +``` |
| 35 | +src/ |
| 36 | + index.ts # Package exports |
| 37 | + credentials/ |
| 38 | + DocumensoApi.credentials.ts # API Key + Base URL credential |
| 39 | + nodes/ |
| 40 | + Documenso/ |
| 41 | + Documenso.node.ts # Main node — resource/operation selector |
| 42 | + Documenso.node.json # Codex metadata |
| 43 | + documenso.svg # Node icon |
| 44 | + GenericFunctions.ts # SDK client factory + error handling |
| 45 | + actions/ |
| 46 | + router.ts # Dispatches resource+operation → per-item execute |
| 47 | + document/ # 10 ops: find, get, create, createAndSend, update, delete, duplicate, download, send, resend |
| 48 | + template/ # 7 ops: find, get, create, update, delete, duplicate, use |
| 49 | + recipient/ # 4 ops: create, get, update, delete |
| 50 | + field/ # 4 ops: create, get, update, delete |
| 51 | + file/ # 4 ops: upload, download, update, delete |
| 52 | + attachment/ # 4 ops: find, create, update, delete |
| 53 | + folder/ # 4 ops: find, create, update, delete |
| 54 | + DocumensoTrigger/ |
| 55 | + DocumensoTrigger.node.ts # Webhook trigger node |
| 56 | +``` |
| 57 | + |
| 58 | +### Key design decisions |
| 59 | + |
| 60 | +- **Envelopes API only**: Document and Template resources both use `client.envelopes.*` under the hood. Document passes `type: "DOCUMENT"`, Template passes `type: "TEMPLATE"`. We do NOT use the older `client.documents.*` or `client.templates.*` SDK methods. |
| 61 | +- **Parameter naming**: Use `documentId`/`templateId` in user-facing params, map to `envelopeId` when calling the SDK. |
| 62 | +- **SDK is bundled**: tsdown inlines `@documenso/sdk-typescript` into the output via `noExternal`. Only `n8n-workflow` is external (provided by n8n at runtime). This means the published package has zero runtime dependencies. |
| 63 | +- **CJS output**: n8n requires CommonJS. tsdown outputs `.js` (not `.cjs`) via `outExtensions`. |
| 64 | + |
| 65 | +### Adding a new operation |
| 66 | + |
| 67 | +1. Create `src/nodes/Documenso/actions/<resource>/<operation>.operation.ts` |
| 68 | +2. Export `description: INodeProperties[]` with `displayOptions: { show: { resource: [...], operation: [...] } }` |
| 69 | +3. Export `async function execute(this: IExecuteFunctions, itemIndex: number): Promise<any>` |
| 70 | +4. Import and register in the resource's `index.ts` (add to `descriptions` array and `operations` map) |
| 71 | +5. If adding a new resource, also register in `Documenso.node.ts`, `router.ts`, and the resource options list |
| 72 | + |
| 73 | +### Operation file pattern |
| 74 | + |
| 75 | +```ts |
| 76 | +import type { IExecuteFunctions, INodeProperties } from "n8n-workflow"; |
| 77 | +import { getDocumensoClient, handleDocumensoError } from "../../GenericFunctions"; |
| 78 | + |
| 79 | +export const description: INodeProperties[] = [ |
| 80 | + { |
| 81 | + displayName: "Document ID", |
| 82 | + name: "documentId", |
| 83 | + type: "string", |
| 84 | + required: true, |
| 85 | + default: "", |
| 86 | + displayOptions: { show: { resource: ["document"], operation: ["myOp"] } }, |
| 87 | + }, |
| 88 | +]; |
| 89 | + |
| 90 | +export async function execute(this: IExecuteFunctions, itemIndex: number): Promise<any> { |
| 91 | + const documentId = this.getNodeParameter("documentId", itemIndex) as string; |
| 92 | + try { |
| 93 | + const client = await getDocumensoClient(this); |
| 94 | + return await client.envelopes.someMethod({ envelopeId: documentId }); |
| 95 | + } catch (error) { |
| 96 | + handleDocumensoError(this, error, itemIndex); |
| 97 | + } |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +### UX conventions |
| 102 | + |
| 103 | +- Use **fixedCollection** with `multipleValues: true` instead of JSON inputs for structured data (recipients, fields, etc.) |
| 104 | +- Use user-friendly operation names: "Send" not "Distribute", "Resend" not "Redistribute" |
| 105 | +- Coordinates are **percentages** (0–100) of page width/height, not pixels |
| 106 | +- Field positioning supports both **placeholder text matching** (`{{signature}}`) and coordinate modes |
| 107 | +- The "Create and Send" operation is the happy path — upload PDF + recipients + fields + send in one step |
| 108 | + |
| 109 | +### Build |
| 110 | + |
| 111 | +tsdown bundles entry points to `dist/`. The `copy` config in `tsdown.config.ts` handles static assets (SVG icon, codex JSON). When adding a new node, add its entry to `tsdown.config.ts` and `package.json`'s `n8n.nodes` array. |
| 112 | + |
| 113 | +### Testing locally |
| 114 | + |
| 115 | +`docker compose up` mounts `dist/` and `package.json` into n8n's custom nodes directory. The node appears in n8n's palette at http://localhost:5678. |
0 commit comments