n8n-nodes-insign 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +430 -0
- package/dist/credentials/InsignApi.credentials.d.ts +12 -0
- package/dist/credentials/InsignApi.credentials.js +116 -0
- package/dist/credentials/InsignApi.credentials.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes/Insign/GenericFunctions.d.ts +21 -0
- package/dist/nodes/Insign/GenericFunctions.js +163 -0
- package/dist/nodes/Insign/GenericFunctions.js.map +1 -0
- package/dist/nodes/Insign/Insign.node.d.ts +5 -0
- package/dist/nodes/Insign/Insign.node.js +615 -0
- package/dist/nodes/Insign/Insign.node.js.map +1 -0
- package/dist/nodes/Insign/insign.svg +8 -0
- package/dist/nodes/InsignTrigger/InsignTrigger.node.d.ts +5 -0
- package/dist/nodes/InsignTrigger/InsignTrigger.node.js +143 -0
- package/dist/nodes/InsignTrigger/InsignTrigger.node.js.map +1 -0
- package/dist/nodes/InsignTrigger/insign.svg +8 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 inSign GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://www.getinsign.com/">
|
|
3
|
+
<img src="./nodes/Insign/insign.svg" width="180" alt="inSign" />
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<h1 align="center">n8n-nodes-insign</h1>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<strong>Add legally binding electronic signatures to any n8n workflow.</strong><br>
|
|
11
|
+
<sub>EU-hosted. eIDAS-compliant. Simple, advanced, and qualified signatures.</sub>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://www.npmjs.com/package/n8n-nodes-insign"><img alt="npm" src="https://img.shields.io/npm/v/n8n-nodes-insign.svg"></a>
|
|
16
|
+
<a href="LICENSE"><img alt="license" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
|
|
17
|
+
<img alt="n8n" src="https://img.shields.io/badge/n8n-1.x-ea4b71.svg">
|
|
18
|
+
<img alt="API" src="https://img.shields.io/badge/inSign-REST%20API-0d6efd.svg">
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Contents
|
|
24
|
+
|
|
25
|
+
- [What you get](#what-you-get)
|
|
26
|
+
- [30-second quickstart](#30-second-quickstart)
|
|
27
|
+
- [Install](#install)
|
|
28
|
+
- [Credentials](#credentials)
|
|
29
|
+
- [Node: inSign (actions)](#node-insign-actions)
|
|
30
|
+
- [Node: inSign Trigger (webhooks)](#node-insign-trigger-webhooks)
|
|
31
|
+
- [Logging, tracing & troubleshooting](#logging-tracing--troubleshooting)
|
|
32
|
+
- [End-to-end example workflow](#end-to-end-example-workflow)
|
|
33
|
+
- [Development & testing](#development--testing)
|
|
34
|
+
- [FAQ](#faq)
|
|
35
|
+
- [License](#license)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## What you get
|
|
40
|
+
|
|
41
|
+
- **1 action node** with 9 operations covering the inSign session lifecycle
|
|
42
|
+
- **1 trigger node** with a stable webhook URL that survives workflow redeploys
|
|
43
|
+
- Inline **base64 document upload** — one call creates the session *and* attaches the PDF
|
|
44
|
+
- An **`Additional Fields (JSON)` escape hatch** on every multi-param operation so you never hit "the node doesn't expose field X"
|
|
45
|
+
- Built-in **trace IDs, timeouts, and debug metadata** for production troubleshooting
|
|
46
|
+
- Works against the **public sandbox with zero setup** — no registration, no API key
|
|
47
|
+
|
|
48
|
+
## 30-second quickstart
|
|
49
|
+
|
|
50
|
+
1. Install `n8n-nodes-insign` from **Settings → Community Nodes** in your n8n instance.
|
|
51
|
+
2. Add an **inSign API** credential — leave the defaults (sandbox OAuth2 client credentials, `controller` / `pwd.insign.sandbox.4561`). Click **Test** → you should see "Connection successful".
|
|
52
|
+
3. Drop an **inSign** node into a workflow, set **Operation = Create Session**, feed it a PDF from an HTTP Request / Read Binary File / Form Trigger node.
|
|
53
|
+
4. Run the workflow. The output contains `sessionid`, `accessURL`, and `jwt`. Open `accessURL` in a browser to sign. Done.
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
### Local — Docker (recommended for trying it out)
|
|
58
|
+
|
|
59
|
+
The fastest path. Requires Docker.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
scripts/run-n8n-docker.sh # builds, packs, installs, runs n8n on :5678
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
See [Try it locally in Docker](#try-it-locally-in-docker) below for details and flags.
|
|
66
|
+
|
|
67
|
+
### Local — against your existing n8n install
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install && npm run build # from this directory
|
|
71
|
+
npm pack # produces n8n-nodes-insign-<ver>.tgz
|
|
72
|
+
mkdir -p ~/.n8n/custom
|
|
73
|
+
cd ~/.n8n/custom
|
|
74
|
+
npm init -y
|
|
75
|
+
npm install /absolute/path/to/n8n-nodes-insign-<ver>.tgz
|
|
76
|
+
n8n start
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Once published to npm
|
|
80
|
+
|
|
81
|
+
Settings → **Community Nodes** → Install → paste `n8n-nodes-insign` → Install. Or from a Docker-based n8n image:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
docker exec -u node -it n8n npm install n8n-nodes-insign
|
|
85
|
+
docker restart n8n
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
See the [official n8n community node docs](https://docs.n8n.io/integrations/community-nodes/installation/) for platform specifics.
|
|
89
|
+
|
|
90
|
+
## Credentials
|
|
91
|
+
|
|
92
|
+
Credential name: **inSign API**. Authentication uses **OAuth2 Client Credentials** (RFC 6749 § 4.4).
|
|
93
|
+
|
|
94
|
+
| Field | Sandbox default | Notes |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| Base URL | `https://sandbox.test.getinsign.show` | Replace with your tenant URL in production |
|
|
97
|
+
| Client ID | `controller` | OAuth2 `client_id`. Sandbox public default. |
|
|
98
|
+
| Client Secret | `pwd.insign.sandbox.4561` | OAuth2 `client_secret`. **Sandbox only — use your production credentials in prod.** |
|
|
99
|
+
|
|
100
|
+
The node requests a token via `POST /oauth2/token` with `grant_type=client_credentials` and sends it back on every call as `Authorization: Bearer <token>`. n8n caches the token and refreshes automatically when it expires (typically 30 min).
|
|
101
|
+
|
|
102
|
+
The credential's **Test** button calls `POST /oauth2/token` directly — if it returns an `access_token` (rather than `error=invalid_client`), the client credentials are valid.
|
|
103
|
+
|
|
104
|
+
## Node: inSign (actions)
|
|
105
|
+
|
|
106
|
+
One resource, nine operations:
|
|
107
|
+
|
|
108
|
+
| Operation | Endpoint | What comes out |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| **Create Session** | `POST /configure/session` | `{ sessionid, jwt, accessURL, ... }` — the PDF is inlined as base64 in a single call |
|
|
111
|
+
| **Get Status** | `POST /get/status` | Full session status + document + annotation metadata |
|
|
112
|
+
| **Check Status** | `POST /get/checkstatus` | Lightweight status — ideal for polling loops |
|
|
113
|
+
| **Send Reminder** | `POST /load/sendManualReminder` | Sends a manual nudge to the signer |
|
|
114
|
+
| **Make Extern** | `POST /extern/beginmulti` | Hands the session to external signers (email/phone) |
|
|
115
|
+
| **Abort Extern** | `POST /extern/abort` | Cancels an external signing flow |
|
|
116
|
+
| **Download Signed Documents** | `POST /get/documents/download` | Binary on the item (default prop `data`). See [Output Mode](#download-output-modes) below. |
|
|
117
|
+
| **Get Audit Trail** | `POST /get/audit` | JSON array of audit events |
|
|
118
|
+
| **Purge Session** | `POST /persistence/purge` | **Irreversible.** Deletes everything. |
|
|
119
|
+
|
|
120
|
+
### The "wide body" escape hatch — `Additional Fields (JSON)`
|
|
121
|
+
|
|
122
|
+
`/configure/session` accepts dozens of optional fields (`guiProperties`, branding, `callbackURL`, `preFilledFields`, signature-level config, watermark, …). The node exposes the ~8 you'll set every time and a single **Additional Fields (JSON)** field that is shallow-merged into the request body. Example:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"createJWT": true,
|
|
127
|
+
"watermarkContent": "#tan# PRODUCTION",
|
|
128
|
+
"guiProperties": { "guiAllowChangeSmsEmail": false, "exitAvailable": true },
|
|
129
|
+
"callbackURL": "https://your-n8n.example.com/webhook/insign"
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Same pattern on **Send Reminder** and **Make Extern**.
|
|
134
|
+
|
|
135
|
+
### Download output modes
|
|
136
|
+
|
|
137
|
+
The **Download Signed Documents** op calls `POST /get/documents/download`. This endpoint almost always returns a **ZIP**, because the archive typically bundles the signed PDF(s) with an audit-report PDF. It only returns a bare PDF in narrow cases — audit-report inclusion disabled *and* a single document in the session (plus a few related edge cases).
|
|
138
|
+
|
|
139
|
+
(inSign also exposes `POST /get/document?sessionid=…&docid=…` to fetch a single document by id, which always returns one PDF. This node does not use that endpoint — use the `HTTP Request` node if you need it.)
|
|
140
|
+
|
|
141
|
+
The op has three output modes:
|
|
142
|
+
|
|
143
|
+
| Mode | PDF response becomes | ZIP response becomes |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| **Auto** (default) | one PDF binary | one ZIP binary |
|
|
146
|
+
| **ZIP (always)** | PDF repackaged as a one-file ZIP | passthrough |
|
|
147
|
+
| **Single Files (unzipped)** | single PDF binary | **one output item per file inside the ZIP** |
|
|
148
|
+
|
|
149
|
+
Use **Single Files** when you want to loop, archive per file, or send each signed contract to a different system. Use **ZIP (always)** when you want a stable container format regardless of document count.
|
|
150
|
+
|
|
151
|
+
### Prominent session behavior toggles
|
|
152
|
+
|
|
153
|
+
Create Session exposes the most common inSign session knobs directly in the node UI (not buried in "Additional Fields"). All fields map 1:1 to OpenAPI properties:
|
|
154
|
+
|
|
155
|
+
| UI label | OpenAPI field | Type |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| Auto-Finish on Last Signature | `workflowFinishAfterLastSign` | boolean |
|
|
158
|
+
| Auto-Complete on External Finish | `externCompleteOnFinish` | boolean |
|
|
159
|
+
| Email Signed Docs to External | `externSendDocsOnFinishCustomer` | boolean |
|
|
160
|
+
| Download Link in Emails | `documentEmailDownload` | boolean |
|
|
161
|
+
| Skip Finish Modal (Owner) | `guiProperties.guiFertigbuttonSkipModalDialog` | boolean |
|
|
162
|
+
| Skip Finish Modal (External) | `guiProperties.guiFertigbuttonSkipModalDialogExtern` | boolean |
|
|
163
|
+
| External User Guidance | `externUserGuidance` | boolean |
|
|
164
|
+
| GDPR Consent Popup | `gdprPopupActive` | boolean |
|
|
165
|
+
| Privacy Policy URL | `privacyLink` | string |
|
|
166
|
+
| Imprint URL | `imprintLink` | string |
|
|
167
|
+
| External CSS / Properties URL | `externalPropertiesURL` | string |
|
|
168
|
+
|
|
169
|
+
Anything not in this list stays accessible through **Additional Fields (JSON)**.
|
|
170
|
+
|
|
171
|
+
### Auto-generated identifiers
|
|
172
|
+
|
|
173
|
+
If **For User** is empty, the node generates `n8n-<ts>-<rand>` for you. The document also gets an auto-generated `id` so you can ignore both unless you need stable values.
|
|
174
|
+
|
|
175
|
+
### Correlation ID + webhook signing
|
|
176
|
+
|
|
177
|
+
The **Create Session** op has two optional fields that wire the session to a signed webhook:
|
|
178
|
+
|
|
179
|
+
| Field | What it does |
|
|
180
|
+
|---|---|
|
|
181
|
+
| **Correlation ID** | Your own identifier for this session. Stored server-side in `customInfo`, appended to the webhook URL as `?cid=<id>`, and surfaced on every `inSign Trigger` item. Leave empty to auto-generate. |
|
|
182
|
+
| **Server-Side Webhook URL** | Base URL inSign will `GET` on every session event. The node appends `?cid=<correlationId>` and, if a **Webhook HMAC Secret** is set on the credential, `&sig=<hmac-sha256(secret, correlationId)>`. The final signed URL is written to `serverSidecallbackURL`. |
|
|
183
|
+
|
|
184
|
+
The `inSign Trigger` node reads `cid` and `sig` from the incoming query, recomputes the HMAC, and rejects calls with a wrong/missing signature (HTTP 403). Turn off **Verify HMAC Signature** on the trigger (or leave the secret empty) to disable.
|
|
185
|
+
|
|
186
|
+
## Node: inSign Trigger (webhooks)
|
|
187
|
+
|
|
188
|
+
A single **stable webhook URL per n8n instance**. You paste it **once** into your inSign webhook configuration (admin UI or proxy) and forget it. It survives workflow edits and redeploys, so inSign callbacks still land even **years** after the session was created.
|
|
189
|
+
|
|
190
|
+
### What the node emits
|
|
191
|
+
|
|
192
|
+
Every inSign callback arrives as one n8n item:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"eventid": "SESSION_FINISHED",
|
|
197
|
+
"sessionid": "abc-123",
|
|
198
|
+
"data": { "...": "original inSign payload..." },
|
|
199
|
+
"raw": { "...": "entire request body, untouched..." },
|
|
200
|
+
"headers": { "x-request-id": "...", "...": "..." },
|
|
201
|
+
"query": { "...": "any query string params..." },
|
|
202
|
+
"receivedAt": "2026-04-23T12:00:00.000Z"
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Optional filters
|
|
207
|
+
|
|
208
|
+
- **Event ID Filter** — comma-separated list, only emit for matching `eventid` values (e.g. `SESSION_FINISHED,SESSION_EXPIRED`). Empty = emit all.
|
|
209
|
+
- **Session ID Filter** — only emit for a specific session. Great when you want many workflows, each listening for its own session.
|
|
210
|
+
|
|
211
|
+
### Registering the URL in inSign
|
|
212
|
+
|
|
213
|
+
- **Self-hosted inSign** — set the webhook URL in your tenant config (admin UI or config file).
|
|
214
|
+
- **Per-session callback URL** — pass `callbackURL` / `serverSidecallbackURL` inside **Additional Fields (JSON)** when creating the session.
|
|
215
|
+
- **Proxy pattern** — see [`docs/data/cf-webhook-worker.js`](../../docs/data/cf-webhook-worker.js), [`deno-deploy-proxy-worker.ts`](../../docs/data/deno-deploy-proxy-worker.ts), or [`valtown-proxy-worker.ts`](../../docs/data/valtown-proxy-worker.ts) in this repo for ready-to-use forwarders.
|
|
216
|
+
|
|
217
|
+
## Logging, tracing & troubleshooting
|
|
218
|
+
|
|
219
|
+
Every action node has an **Options** collection with:
|
|
220
|
+
|
|
221
|
+
| Option | Default | Purpose |
|
|
222
|
+
|---|---|---|
|
|
223
|
+
| **Request Timeout (ms)** | `30000` | How long to wait before giving up on inSign |
|
|
224
|
+
| **Trace ID** | auto | Sent as `X-Request-Id`. Correlates n8n runs with inSign server logs. Leave empty to auto-generate one per call. |
|
|
225
|
+
| **Include Request Metadata** | `false` | When on, output items get a `_meta` object: `{ method, path, durationMs, traceId, url }`. Turn on while debugging, off in production. |
|
|
226
|
+
|
|
227
|
+
### Debug logs
|
|
228
|
+
|
|
229
|
+
The node logs to n8n's standard logger:
|
|
230
|
+
|
|
231
|
+
- `debug` level — every request start and success, with redacted body (base64 document payloads are replaced with `<base64:N chars>`)
|
|
232
|
+
- `warn` level — every failure, with trace ID and duration
|
|
233
|
+
|
|
234
|
+
Enable them by starting n8n with `N8N_LOG_LEVEL=debug`:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
N8N_LOG_LEVEL=debug n8n start
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Then grep your logs for `[inSign]` or for a specific trace ID.
|
|
241
|
+
|
|
242
|
+
### Common errors
|
|
243
|
+
|
|
244
|
+
| Symptom | Cause | Fix |
|
|
245
|
+
|---|---|---|
|
|
246
|
+
| `HTTP 599 {"error":599,"message":"Access is denied"}` | Missing or wrong client credentials | Sandbox uses `controller` / `pwd.insign.sandbox.4561`. For production, verify `client_id`/`client_secret` with your inSign admin. |
|
|
247
|
+
| `inSign /oauth2/token did not return an access_token` | Wrong client credentials, or `/oauth2/token` not reachable at the configured Base URL | Confirm Base URL is a tenant root (not a path), and that the client credentials are valid. |
|
|
248
|
+
| `HTTP 400 {"path":"foruser","message":"must not be null"}` | Empty `foruser` and auto-gen disabled | Leave **For User** empty (auto-generates) or set a value. |
|
|
249
|
+
| `HTTP 400 {"path":"documents[0].id","message":"must not be empty"}` | Custom JSON body overwrote the auto-id | When using **Additional Fields** to set `documents`, include `id` on each document. |
|
|
250
|
+
| Trigger never fires | Webhook URL not registered, or n8n not reachable from inSign | Hit the trigger URL yourself with `curl -X POST ...` to confirm n8n-side setup; then verify inSign actually calls it (check tenant logs, or point it through a [proxy worker](../../docs/data/cf-webhook-worker.js) to log inbound traffic). |
|
|
251
|
+
| `Timeout` under load | Large PDFs or slow network | Increase **Request Timeout** in the node's **Options**. |
|
|
252
|
+
|
|
253
|
+
### "Continue On Fail"
|
|
254
|
+
|
|
255
|
+
Standard n8n feature — turn it on in the node's **Settings** pane to collect errors as items instead of failing the run. The node returns `{ error: "..." }` items with the item `pairedItem` preserved so you can correlate to inputs downstream.
|
|
256
|
+
|
|
257
|
+
## Quickstart workflow (bundled)
|
|
258
|
+
|
|
259
|
+
A complete demo workflow ships in [`examples/quickstart-workflow.json`](examples/quickstart-workflow.json). Import it via the n8n UI:
|
|
260
|
+
|
|
261
|
+
1. Open `http://localhost:5678/`
|
|
262
|
+
2. Workflows → ⋯ (top right) → **Import from File**
|
|
263
|
+
3. Pick `examples/quickstart-workflow.json`
|
|
264
|
+
|
|
265
|
+
Or via CLI: `docker cp examples/quickstart-workflow.json n8n-insign-dev:/tmp/q.json && docker exec n8n-insign-dev n8n import:workflow --input=/tmp/q.json --userId=<your-user-id>`.
|
|
266
|
+
|
|
267
|
+
Shape of the workflow:
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
┌─ Start (manual trigger)
|
|
271
|
+
│ │
|
|
272
|
+
│ ▼
|
|
273
|
+
│ HTTP Request — download sample.pdf from GitHub
|
|
274
|
+
│ │
|
|
275
|
+
│ ▼
|
|
276
|
+
│ Code — assign correlationId = demo-<ts>-<rand>
|
|
277
|
+
│ │
|
|
278
|
+
│ ▼
|
|
279
|
+
│ inSign: Create Session (correlationId → customInfo; signed serverSidecallbackURL)
|
|
280
|
+
│ │
|
|
281
|
+
│ ▼
|
|
282
|
+
│ Code — track in $getWorkflowStaticData: { cid → { sessionid, finished:false, createdAt } }
|
|
283
|
+
│
|
|
284
|
+
├─ Thanks Page Webhook (GET /webhook/signed-thanks)
|
|
285
|
+
│ │
|
|
286
|
+
│ ▼
|
|
287
|
+
│ Respond to Webhook — returns inline HTML "Thanks for signing!"
|
|
288
|
+
│ (linked from Create Session's `callbackURL` so the signer's browser lands here when done)
|
|
289
|
+
│
|
|
290
|
+
├─ inSign Trigger (stable webhook, eventIdFilter="VORGANGABGESCHLOSSEN")
|
|
291
|
+
│ │
|
|
292
|
+
│ ▼
|
|
293
|
+
│ inSign: Get Status
|
|
294
|
+
│ │
|
|
295
|
+
│ ▼
|
|
296
|
+
│ inSign: Download Signed (Output Mode = Single Files)
|
|
297
|
+
│ │
|
|
298
|
+
│ ▼
|
|
299
|
+
│ Code — mark finished=true in static data
|
|
300
|
+
│ │
|
|
301
|
+
│ ▼
|
|
302
|
+
│ inSign: Purge
|
|
303
|
+
│ │
|
|
304
|
+
│ ▼
|
|
305
|
+
│ Code — remove entry from static data
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Before running:
|
|
309
|
+
1. Create the **inSign API** credential and attach it to all inSign nodes in the workflow.
|
|
310
|
+
2. Click **Execute** on **Setup: Create Data Table** once — this creates the `insign_sessions` table in n8n's Data Tables.
|
|
311
|
+
3. Open the `inSign Trigger` node, copy the **Production URL**, then paste it into the `Server-Side Webhook URL` field on the `inSign: Create Session` node (placeholder in the JSON is `PASTE_YOUR_TRIGGER_PRODUCTION_URL_HERE`).
|
|
312
|
+
4. If you want signed webhooks end-to-end, fill in **Webhook HMAC Secret** on the credential.
|
|
313
|
+
5. Activate the workflow (so the trigger is live) and press **Execute** on the manual **Start**.
|
|
314
|
+
|
|
315
|
+
**Session tracking storage.** The quickstart uses n8n's built-in [`$getWorkflowStaticData('global')`](https://docs.n8n.io/code/builtin/overview/) — workflow-scoped key-value data that persists across executions. Zero external dependencies.
|
|
316
|
+
|
|
317
|
+
If you prefer n8n's native **Data Table** node (`n8n-nodes-base.dataTable`, SQLite-backed, shipped with n8n 1.x+):
|
|
318
|
+
1. Create a data table manually in the n8n UI (Settings → Data Tables → Add) named `insign_sessions` with columns `correlationId` (string), `sessionid` (string), `accessURL` (string), `finished` (boolean), `createdAt` (string), `finishedAt` (string).
|
|
319
|
+
2. Replace the three "Track / Mark / Remove" Code nodes with Data Table `row.upsert`, `row.update`, `row.delete` operations keyed on `correlationId`.
|
|
320
|
+
|
|
321
|
+
We ship the Code-node version because a Data Table workflow needs the table to exist *at import time* — n8n's JSON import validates the `resourceMapper` column mapping against the live schema and rejects workflows that reference tables not yet created on the target instance.
|
|
322
|
+
|
|
323
|
+
Swap either approach for Postgres / Redis / MongoDB nodes if you need cross-workflow visibility or long-term queryability.
|
|
324
|
+
|
|
325
|
+
## End-to-end example workflow
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
[Webhook trigger: form submit]
|
|
329
|
+
│
|
|
330
|
+
▼
|
|
331
|
+
[Read Binary File: contract.pdf]
|
|
332
|
+
│
|
|
333
|
+
▼
|
|
334
|
+
[inSign: Create Session] ← output: sessionid, jwt, accessURL
|
|
335
|
+
│
|
|
336
|
+
▼
|
|
337
|
+
[Send Email: "Sign here: {{ $json.accessURL }}"]
|
|
338
|
+
│
|
|
339
|
+
▼
|
|
340
|
+
(wait for callback on a separate workflow)
|
|
341
|
+
|
|
342
|
+
[inSign Trigger] ← stable webhook URL, set in inSign tenant config
|
|
343
|
+
│
|
|
344
|
+
▼
|
|
345
|
+
[IF: $json.eventid === "SESSION_FINISHED"]
|
|
346
|
+
│
|
|
347
|
+
▼
|
|
348
|
+
[inSign: Download Signed Documents]
|
|
349
|
+
│
|
|
350
|
+
▼
|
|
351
|
+
[Write Binary File + Send to archive / CRM]
|
|
352
|
+
│
|
|
353
|
+
▼
|
|
354
|
+
[inSign: Purge Session]
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Try it locally in Docker
|
|
358
|
+
|
|
359
|
+
A one-command launcher builds the package and starts n8n with the node pre-installed:
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
scripts/run-n8n-docker.sh # http://localhost:5678
|
|
363
|
+
scripts/run-n8n-docker.sh --port 5679
|
|
364
|
+
scripts/run-n8n-docker.sh --rebuild # re-pack after code changes
|
|
365
|
+
scripts/run-n8n-docker.sh --down # stop (data preserved)
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
What it does:
|
|
369
|
+
|
|
370
|
+
1. `npm run build` in this package
|
|
371
|
+
2. `npm pack` to produce a tarball
|
|
372
|
+
3. Installs the tarball into `.n8n-docker/custom/`
|
|
373
|
+
4. Starts `n8nio/n8n:latest` with `.n8n-docker/data` mounted as `~/.n8n` (workflows and credentials persist) and `.n8n-docker/custom` as `~/.n8n/custom` (so n8n picks up the node)
|
|
374
|
+
|
|
375
|
+
Logs: `docker logs -f n8n-insign-dev`. `N8N_LOG_LEVEL=debug` is on by default so every `[inSign]` request line shows up.
|
|
376
|
+
|
|
377
|
+
After logout/stop, re-run the script to pick up code changes — pass `--rebuild` if you skipped `npm install` between runs.
|
|
378
|
+
|
|
379
|
+
## Development & testing
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
npm install # install deps
|
|
383
|
+
npm run build # tsc + copy SVG icons to dist/
|
|
384
|
+
npm run lint # eslint with n8n-nodes-base rules
|
|
385
|
+
npm test # build + unit tests (pure helpers, no network)
|
|
386
|
+
npm run test:e2e # build + end-to-end tests against the public sandbox
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Environment variables used by the e2e tests (all optional):
|
|
390
|
+
|
|
391
|
+
| Var | Default |
|
|
392
|
+
|---|---|
|
|
393
|
+
| `INSIGN_E2E` | must be `1` to run (otherwise the tests skip) |
|
|
394
|
+
| `INSIGN_BASE_URL` | `https://sandbox.test.getinsign.show` |
|
|
395
|
+
| `INSIGN_CLIENT_ID` | `controller` |
|
|
396
|
+
| `INSIGN_CLIENT_SECRET` | `pwd.insign.sandbox.4561` |
|
|
397
|
+
| `INSIGN_SAMPLE_PDF` | `../../docs/data/sample.pdf` (relative to this package) |
|
|
398
|
+
|
|
399
|
+
The e2e test creates a session, exercises status / extern / audit flows, and always purges the session in a `finally` block. It's safe to run repeatedly.
|
|
400
|
+
|
|
401
|
+
### Testing against your own n8n
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
npm run build
|
|
405
|
+
npm link # registers the package globally
|
|
406
|
+
mkdir -p ~/.n8n/custom && cd ~/.n8n/custom
|
|
407
|
+
npm link n8n-nodes-insign # symlinks into n8n's custom folder
|
|
408
|
+
n8n start
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## FAQ
|
|
412
|
+
|
|
413
|
+
**Does this work with n8n Cloud?**
|
|
414
|
+
Yes — install it from **Settings → Community Nodes**.
|
|
415
|
+
|
|
416
|
+
**What about production tenants?**
|
|
417
|
+
Change the credential's **Base URL** to your tenant, and replace the sandbox client credentials with your production `client_id` / `client_secret`. Everything else works identically.
|
|
418
|
+
|
|
419
|
+
**Can I upload multiple documents?**
|
|
420
|
+
Yes — put them in **Additional Fields (JSON)** as `{ "documents": [ {...}, {...} ] }`. The merge is shallow, so your array replaces the auto-generated single-document array.
|
|
421
|
+
|
|
422
|
+
**Why doesn't the Trigger node register itself with inSign?**
|
|
423
|
+
Because inSign callbacks can fire years after session creation, the URL must outlive workflow edits and redeploys. Registering a URL that changes every redeploy would drop callbacks on the floor. Set the URL once, forget it.
|
|
424
|
+
|
|
425
|
+
**How do I correlate an n8n run with an inSign server log entry?**
|
|
426
|
+
Enable **Include Request Metadata** in Options. The `_meta.traceId` value is the `X-Request-Id` header sent to inSign — your tenant operator can grep server logs for it.
|
|
427
|
+
|
|
428
|
+
## License
|
|
429
|
+
|
|
430
|
+
[MIT](LICENSE) © inSign GmbH
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IAuthenticateGeneric, ICredentialDataDecryptedObject, ICredentialTestRequest, ICredentialType, IHttpRequestHelper, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class InsignApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
documentationUrl: string;
|
|
6
|
+
properties: INodeProperties[];
|
|
7
|
+
preAuthentication(this: IHttpRequestHelper, credentials: ICredentialDataDecryptedObject): Promise<{
|
|
8
|
+
sessionToken: string;
|
|
9
|
+
}>;
|
|
10
|
+
authenticate: IAuthenticateGeneric;
|
|
11
|
+
test: ICredentialTestRequest;
|
|
12
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InsignApi = void 0;
|
|
4
|
+
class InsignApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'insignApi';
|
|
7
|
+
this.displayName = 'inSign API';
|
|
8
|
+
this.documentationUrl = 'https://getinsign.github.io/insign-getting-started/explorer.html';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'Base URL',
|
|
12
|
+
name: 'baseUrl',
|
|
13
|
+
type: 'string',
|
|
14
|
+
default: 'https://sandbox.test.getinsign.show',
|
|
15
|
+
description: 'Sandbox: https://sandbox.test.getinsign.show. Production: your tenant URL.',
|
|
16
|
+
placeholder: 'https://sandbox.test.getinsign.show',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
displayName: 'Client ID',
|
|
20
|
+
name: 'clientId',
|
|
21
|
+
type: 'string',
|
|
22
|
+
default: 'controller',
|
|
23
|
+
description: 'OAuth2 client_id. Sandbox default: "controller".',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
displayName: 'Client Secret',
|
|
27
|
+
name: 'clientSecret',
|
|
28
|
+
type: 'string',
|
|
29
|
+
typeOptions: { password: true },
|
|
30
|
+
default: 'pwd.insign.sandbox.4561',
|
|
31
|
+
description: 'OAuth2 client_secret. Sandbox default: "pwd.insign.sandbox.4561".',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
displayName: 'Webhook HMAC Secret (Optional)',
|
|
35
|
+
name: 'webhookSecret',
|
|
36
|
+
type: 'string',
|
|
37
|
+
typeOptions: { password: true },
|
|
38
|
+
default: '',
|
|
39
|
+
description: 'Shared secret used to sign/verify inSign webhook callbacks. When set, the Create Session node appends an HMAC signature to the serverSidecallbackURL, and the inSign Trigger rejects calls whose signature does not match. Leave empty to disable signing.',
|
|
40
|
+
},
|
|
41
|
+
// sessionToken is populated by preAuthentication() and used by the
|
|
42
|
+
// authenticate() expression below. Hidden from the credential UI.
|
|
43
|
+
{
|
|
44
|
+
displayName: 'Session Token',
|
|
45
|
+
name: 'sessionToken',
|
|
46
|
+
type: 'hidden',
|
|
47
|
+
default: '',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
this.authenticate = {
|
|
51
|
+
type: 'generic',
|
|
52
|
+
properties: {
|
|
53
|
+
headers: {
|
|
54
|
+
Authorization: '=Bearer {{$credentials.sessionToken}}',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
// The test calls /oauth2/token directly so it verifies the client credentials
|
|
59
|
+
// themselves, independent of whether preAuthentication has run yet.
|
|
60
|
+
this.test = {
|
|
61
|
+
request: {
|
|
62
|
+
baseURL: '={{$credentials.baseUrl}}',
|
|
63
|
+
url: '/oauth2/token',
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: {
|
|
66
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
67
|
+
// explicitly drop the Bearer header from authenticate() — the token
|
|
68
|
+
// endpoint must not receive a partial "Bearer " value
|
|
69
|
+
Authorization: '',
|
|
70
|
+
},
|
|
71
|
+
body: '=grant_type=client_credentials&client_id={{$credentials.clientId}}&client_secret={{$credentials.clientSecret}}',
|
|
72
|
+
},
|
|
73
|
+
rules: [
|
|
74
|
+
{
|
|
75
|
+
type: 'responseSuccessBody',
|
|
76
|
+
properties: {
|
|
77
|
+
key: 'error',
|
|
78
|
+
value: 'invalid_client',
|
|
79
|
+
message: 'inSign rejected the client credentials (error=invalid_client). Double-check Client ID and Client Secret against your tenant config.',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: 'responseSuccessBody',
|
|
84
|
+
properties: {
|
|
85
|
+
key: 'error',
|
|
86
|
+
value: 'unauthorized_client',
|
|
87
|
+
message: 'inSign rejected the client grant (error=unauthorized_client). The client is not allowed to use the client_credentials grant for this tenant.',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// Fetches a Bearer token via the OAuth2 Client Credentials grant.
|
|
94
|
+
// n8n caches the result and re-runs this when the cached credential
|
|
95
|
+
// expires or an authenticated request returns 401.
|
|
96
|
+
async preAuthentication(credentials) {
|
|
97
|
+
const baseUrl = credentials.baseUrl.replace(/\/+$/, '');
|
|
98
|
+
const options = {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
url: `${baseUrl}/oauth2/token`,
|
|
101
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
102
|
+
body: new URLSearchParams({
|
|
103
|
+
grant_type: 'client_credentials',
|
|
104
|
+
client_id: credentials.clientId,
|
|
105
|
+
client_secret: credentials.clientSecret,
|
|
106
|
+
}).toString(),
|
|
107
|
+
};
|
|
108
|
+
const res = (await this.helpers.httpRequest(options));
|
|
109
|
+
if (!res?.access_token) {
|
|
110
|
+
throw new Error('inSign /oauth2/token did not return an access_token');
|
|
111
|
+
}
|
|
112
|
+
return { sessionToken: res.access_token };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
exports.InsignApi = InsignApi;
|
|
116
|
+
//# sourceMappingURL=InsignApi.credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InsignApi.credentials.js","sourceRoot":"","sources":["../../credentials/InsignApi.credentials.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InsignApi = exports.InsignTrigger = exports.Insign = void 0;
|
|
4
|
+
// Barrel entry referenced by package.json "main". n8n itself loads the nodes
|
|
5
|
+
// and the credential through the "n8n" field in package.json; this export
|
|
6
|
+
// exists so the package also resolves cleanly when require()-d directly.
|
|
7
|
+
var Insign_node_1 = require("./nodes/Insign/Insign.node");
|
|
8
|
+
Object.defineProperty(exports, "Insign", { enumerable: true, get: function () { return Insign_node_1.Insign; } });
|
|
9
|
+
var InsignTrigger_node_1 = require("./nodes/InsignTrigger/InsignTrigger.node");
|
|
10
|
+
Object.defineProperty(exports, "InsignTrigger", { enumerable: true, get: function () { return InsignTrigger_node_1.InsignTrigger; } });
|
|
11
|
+
var InsignApi_credentials_1 = require("./credentials/InsignApi.credentials");
|
|
12
|
+
Object.defineProperty(exports, "InsignApi", { enumerable: true, get: function () { return InsignApi_credentials_1.InsignApi; } });
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;AAAA,6EAA6E;AAC7E,0EAA0E;AAC1E,yEAAyE;AACzE,0DAAoD;AAA3C,qGAAA,MAAM,OAAA;AACf,+EAAyE;AAAhE,mHAAA,aAAa,OAAA;AACtB,6EAAgE;AAAvD,kHAAA,SAAS,OAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { IExecuteFunctions } from 'n8n-workflow';
|
|
2
|
+
export interface RequestExtras {
|
|
3
|
+
timeout?: number;
|
|
4
|
+
traceId?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function insignRequest(this: IExecuteFunctions, method: 'GET' | 'POST', path: string, body?: Record<string, unknown>, encoding?: 'arraybuffer' | 'json', extras?: RequestExtras): Promise<{
|
|
7
|
+
data: any;
|
|
8
|
+
meta: RequestMeta;
|
|
9
|
+
}>;
|
|
10
|
+
export declare function isInsignErrorBody(body: unknown): body is {
|
|
11
|
+
error: unknown;
|
|
12
|
+
message?: string;
|
|
13
|
+
};
|
|
14
|
+
export interface RequestMeta {
|
|
15
|
+
method: string;
|
|
16
|
+
path: string;
|
|
17
|
+
durationMs: number;
|
|
18
|
+
traceId: string;
|
|
19
|
+
url: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function mergeAdditionalFields(base: Record<string, unknown>, extra?: string | object): Record<string, unknown>;
|