@hostwebhook/node 1.6.0 → 1.6.1

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/README.md CHANGED
@@ -1,276 +1,276 @@
1
- # @hostwebhook/node
2
-
3
- > **Early Access** — This SDK is in early access. The API may change between versions. We'd love your feedback — [report an issue](https://www.hostwebhook.com/#issues) if you run into anything.
4
-
5
- Official Node.js SDK for [HostWebhook](https://www.hostwebhook.com) — verify webhook signatures and send webhooks through the platform.
6
-
7
- ## Install
8
-
9
- ```sh
10
- npm install @hostwebhook/node
11
- ```
12
-
13
- Requires Node.js 18+.
14
-
15
- ## Verify Webhook Signatures
16
-
17
- HostWebhook signs every delivery with HMAC-SHA256. Use these helpers to verify authenticity and prevent replay attacks.
18
-
19
- ### Express
20
-
21
- ```ts
22
- import express from 'express';
23
- import { expressMiddleware } from '@hostwebhook/node';
24
-
25
- const app = express();
26
-
27
- app.post('/webhooks',
28
- express.raw({ type: 'application/json' }),
29
- expressMiddleware(process.env.SIGNING_SECRET!),
30
- (req, res) => {
31
- const payload = JSON.parse(req.body.toString());
32
- // process payload...
33
- res.json({ received: true });
34
- },
35
- );
36
- ```
37
-
38
- > **Important:** Use `express.raw()` so `req.body` is a Buffer, not parsed JSON.
39
-
40
- ### NestJS
41
-
42
- ```ts
43
- import { Controller, Post, UseGuards, Req } from '@nestjs/common';
44
- import { createNestGuard } from '@hostwebhook/node';
45
-
46
- const WebhookGuard = createNestGuard(process.env.SIGNING_SECRET!);
47
-
48
- @Controller('webhooks')
49
- export class WebhooksController {
50
- @Post()
51
- @UseGuards(WebhookGuard)
52
- handle(@Req() req: Request) {
53
- return { received: true };
54
- }
55
- }
56
- ```
57
-
58
- ### Fastify
59
-
60
- ```ts
61
- import Fastify from 'fastify';
62
- import { fastifyHook } from '@hostwebhook/node';
63
-
64
- const app = Fastify();
65
- app.addContentTypeParser('application/json', { parseAs: 'buffer' }, (req, body, done) => done(null, body));
66
-
67
- app.post('/webhooks', {
68
- preHandler: fastifyHook(process.env.SIGNING_SECRET!),
69
- }, (req, reply) => {
70
- reply.send({ received: true });
71
- });
72
- ```
73
-
74
- ### Manual Verification
75
-
76
- ```ts
77
- import { verify, VerificationError } from '@hostwebhook/node';
78
-
79
- try {
80
- verify(rawBody, request.headers, secret);
81
- // signature valid
82
- } catch (err) {
83
- if (err instanceof VerificationError) {
84
- // invalid — reject the request
85
- }
86
- }
87
- ```
88
-
89
- Options: `verify(payload, headers, secret, { maxAgeSec: 300 })` — default 300s (5 min). Set `0` to disable replay protection.
90
-
91
- ## Send Webhooks
92
-
93
- Send webhook payloads through HostWebhook for reliable delivery with retries and monitoring.
94
-
95
- ### Fire & forget (default)
96
-
97
- ```ts
98
- import { HostWebhook } from '@hostwebhook/node';
99
-
100
- const hw = new HostWebhook({ token: 'your_ingress_token' });
101
-
102
- // Returns immediately with the event ID — you don't know what happened in the pipeline
103
- const { eventId } = await hw.send({ event: 'order.created', orderId: '123' });
104
- ```
105
-
106
- ### Wait for pipeline result (blocking)
107
-
108
- Use `waitForResult` to wait for the full pipeline to complete. The SDK opens an SSE stream behind the scenes and returns the result with all pipeline steps. **This blocks until the pipeline finishes** (~120s max).
109
-
110
- ```ts
111
- const result = await hw.send(
112
- { event: 'order.created', orderId: '123' },
113
- { waitForResult: true },
114
- );
115
-
116
- console.log(result.syncStatus); // 200 = delivered, 422 = validation failed, 502 = failed, 408 = timeout
117
- console.log(result.steps); // array of pipeline steps with nodeType, status, durationMs
118
- console.log(result.response); // response body from target URL
119
- ```
120
-
121
- ### Non-blocking result (recommended for UIs)
122
-
123
- Use `onResult` to get the pipeline result in a background callback. `send()` returns immediately so your UI never blocks. Perfect for forms and user-facing apps.
124
-
125
- ```ts
126
- const { eventId } = await hw.send(
127
- { event: 'order.created', orderId: '123' },
128
- {
129
- onResult: (result) => {
130
- // Fires when the pipeline completes (via SSE in background)
131
- if (result.awaitingApproval) {
132
- showToast('Awaiting approval from ' + result.approvalNodeName);
133
- } else if (result.syncStatus === 200) {
134
- showToast('Delivered successfully');
135
- } else {
136
- showToast('Pipeline failed: ' + result.error);
137
- }
138
- },
139
- onStep: (step) => {
140
- // Optional: fires for each pipeline node as it executes
141
- updateProgress(step.nodeName, step.status);
142
- },
143
- },
144
- );
145
- // UI shows "Sent!" immediately here — never blocks
146
- ```
147
-
148
- > **Important:** `onResult` requires the endpoint to be in **None** response mode (async). In Sync mode, the server holds the HTTP connection open until the pipeline completes, which blocks `send()` regardless of callbacks. See [Choosing the right mode](#choosing-the-right-mode) below.
149
-
150
- ### Deduplication detection
151
-
152
- When an event is deduplicated, the response includes `deduplicated: true`:
153
-
154
- ```ts
155
- const result = await hw.send({ event: 'order.created', orderId: '123' });
156
-
157
- if (result.deduplicated) {
158
- console.log('Duplicate! Original event:', result.originalEventId);
159
- }
160
- ```
161
-
162
- ### Batch
163
-
164
- ```ts
165
- const results = await hw.sendBatch([
166
- { event: 'user.signup', userId: '1' },
167
- { event: 'user.signup', userId: '2' },
168
- ]);
169
- ```
170
-
171
- ### Standalone Function
172
-
173
- ```ts
174
- import { send } from '@hostwebhook/node';
175
-
176
- const { eventId } = await send(
177
- { token: 'your_ingress_token' },
178
- { event: 'test', data: 'hello' },
179
- );
180
- ```
181
-
182
- ## Choosing the right mode
183
-
184
- Your endpoint has a **Response Mode** setting (None or Sync) that affects how `send()` behaves:
185
-
186
- | Scenario | Endpoint mode | SDK option | Behavior |
187
- |----------|--------------|------------|----------|
188
- | **Forms / UIs** | None | `onResult` | `send()` returns instantly. Pipeline result arrives in background callback. **UI never blocks.** |
189
- | **Backend scripts** | None | `waitForResult` | `send()` blocks until pipeline completes via SSE (~120s max). |
190
- | **API proxy / Stripe** | Sync | *(none)* | `send()` blocks until pipeline completes via HTTP (~120s max). Result is inline in the response. |
191
- | **Testing (Postman)** | Sync | *(none)* | Single request, response includes full pipeline result. |
192
-
193
- > **Warning: Never use Sync mode for user-facing forms or UIs.** Sync mode holds the HTTP connection open on the server, which blocks `send()` and freezes the UI until the pipeline completes. If the pipeline has approval nodes, merge nodes, or long delays, the SDK will timeout. Use **None** mode with `onResult` instead — the user sees "Sent!" immediately and the pipeline result arrives in the background.
194
-
195
- ### When to use Sync mode
196
-
197
- - The caller is a **server or script**, not a human looking at a UI
198
- - The caller **needs the pipeline result in the same HTTP request** (e.g., Stripe expects a specific response body)
199
- - The pipeline is **fast and linear** (filter -> transform -> action) with no human-gated nodes (approval, merge)
200
-
201
- ### When to use None mode + onResult
202
-
203
- - The caller is a **form, UI, or frontend app**
204
- - You want **instant feedback** ("Sent!") without waiting for the pipeline
205
- - The pipeline has **approval nodes, merge nodes, or delays**
206
- - You want **real-time progress** via `onStep` callbacks
207
-
208
- ## Configuration
209
-
210
- | Option | Type | Default | Description |
211
- |--------|------|---------|-------------|
212
- | `token` | `string` | *required* | Endpoint ingress token |
213
- | `baseUrl` | `string` | `https://api.hostwebhook.com` | API base URL |
214
- | `signingSecret` | `string` | — | Signs outgoing requests with HMAC-SHA256 |
215
- | `signature` | `StaticSignature` | — | Static secret signature (alternative to HMAC) |
216
- | `defaultHeaders` | `Record<string, string>` | — | Headers added to every request |
217
- | `timeoutMs` | `number` | `10000` | Request timeout in ms |
218
-
219
- ### SendOptions
220
-
221
- | Option | Type | Default | Description |
222
- |--------|------|---------|-------------|
223
- | `headers` | `Record<string, string>` | — | Per-request headers |
224
- | `contentType` | `string` | `application/json` | Override content type |
225
- | `rawBody` | `string \| Buffer` | — | Send raw body instead of JSON |
226
- | `waitForResult` | `boolean` | `false` | Wait for pipeline result via SSE stream (blocking) |
227
- | `onResult` | `(result) => void` | — | Non-blocking pipeline result callback via SSE |
228
- | `onStep` | `(step) => void` | — | Real-time pipeline step callback (with `onResult` or `waitForResult`) |
229
-
230
- ## Error Handling
231
-
232
- ```ts
233
- import { SendError, VerificationError } from '@hostwebhook/node';
234
-
235
- // Send errors
236
- try {
237
- await hw.send(payload);
238
- } catch (err) {
239
- if (err instanceof SendError) {
240
- console.log(err.statusCode, err.responseBody);
241
- }
242
- }
243
-
244
- // Verification errors
245
- try {
246
- verify(body, headers, secret);
247
- } catch (err) {
248
- if (err instanceof VerificationError) {
249
- console.log(err.message);
250
- }
251
- }
252
- ```
253
-
254
- ## Signature Format
255
-
256
- ```
257
- X-HostWebhook-Signature: t=<timestamp_ms>,v1=<hmac_hex>
258
- ```
259
-
260
- HMAC computed as: `HMAC-SHA256(secret, "${timestamp}.${rawBody}")`
261
-
262
- ## Support
263
-
264
- Found a bug or have a feature request? [Report an issue](https://www.hostwebhook.com/#issues) on our website.
265
-
266
- ## Privacy
267
-
268
- Your webhook data is yours. HostWebhook is built with privacy as a core principle:
269
-
270
- - **Your payloads stay between you and your endpoints** — we never sell, share, or use your data for training.
271
- - **Encryption in transit** — all traffic between the SDK, the platform, and your endpoints is sent over TLS.
272
- - **Minimal data retention** — event payloads are stored only for delivery and debugging, and are automatically purged based on your plan's retention window.
273
-
274
- ## License
275
-
276
- MIT
1
+ # @hostwebhook/node
2
+
3
+ > **Early Access** — This SDK is in early access. The API may change between versions. We'd love your feedback — [report an issue](https://www.hostwebhook.com/#issues) if you run into anything.
4
+
5
+ Official Node.js SDK for [HostWebhook](https://www.hostwebhook.com) — verify webhook signatures and send webhooks through the platform.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ npm install @hostwebhook/node
11
+ ```
12
+
13
+ Requires Node.js 18+.
14
+
15
+ ## Verify Webhook Signatures
16
+
17
+ HostWebhook signs every delivery with HMAC-SHA256. Use these helpers to verify authenticity and prevent replay attacks.
18
+
19
+ ### Express
20
+
21
+ ```ts
22
+ import express from 'express';
23
+ import { expressMiddleware } from '@hostwebhook/node';
24
+
25
+ const app = express();
26
+
27
+ app.post('/webhooks',
28
+ express.raw({ type: 'application/json' }),
29
+ expressMiddleware(process.env.SIGNING_SECRET!),
30
+ (req, res) => {
31
+ const payload = JSON.parse(req.body.toString());
32
+ // process payload...
33
+ res.json({ received: true });
34
+ },
35
+ );
36
+ ```
37
+
38
+ > **Important:** Use `express.raw()` so `req.body` is a Buffer, not parsed JSON.
39
+
40
+ ### NestJS
41
+
42
+ ```ts
43
+ import { Controller, Post, UseGuards, Req } from '@nestjs/common';
44
+ import { createNestGuard } from '@hostwebhook/node';
45
+
46
+ const WebhookGuard = createNestGuard(process.env.SIGNING_SECRET!);
47
+
48
+ @Controller('webhooks')
49
+ export class WebhooksController {
50
+ @Post()
51
+ @UseGuards(WebhookGuard)
52
+ handle(@Req() req: Request) {
53
+ return { received: true };
54
+ }
55
+ }
56
+ ```
57
+
58
+ ### Fastify
59
+
60
+ ```ts
61
+ import Fastify from 'fastify';
62
+ import { fastifyHook } from '@hostwebhook/node';
63
+
64
+ const app = Fastify();
65
+ app.addContentTypeParser('application/json', { parseAs: 'buffer' }, (req, body, done) => done(null, body));
66
+
67
+ app.post('/webhooks', {
68
+ preHandler: fastifyHook(process.env.SIGNING_SECRET!),
69
+ }, (req, reply) => {
70
+ reply.send({ received: true });
71
+ });
72
+ ```
73
+
74
+ ### Manual Verification
75
+
76
+ ```ts
77
+ import { verify, VerificationError } from '@hostwebhook/node';
78
+
79
+ try {
80
+ verify(rawBody, request.headers, secret);
81
+ // signature valid
82
+ } catch (err) {
83
+ if (err instanceof VerificationError) {
84
+ // invalid — reject the request
85
+ }
86
+ }
87
+ ```
88
+
89
+ Options: `verify(payload, headers, secret, { maxAgeSec: 300 })` — default 300s (5 min). Set `0` to disable replay protection.
90
+
91
+ ## Send Webhooks
92
+
93
+ Send webhook payloads through HostWebhook for reliable delivery with retries and monitoring.
94
+
95
+ ### Fire & forget (default)
96
+
97
+ ```ts
98
+ import { HostWebhook } from '@hostwebhook/node';
99
+
100
+ const hw = new HostWebhook({ token: 'your_ingress_token' });
101
+
102
+ // Returns immediately with the event ID — you don't know what happened in the pipeline
103
+ const { eventId } = await hw.send({ event: 'order.created', orderId: '123' });
104
+ ```
105
+
106
+ ### Wait for pipeline result (blocking)
107
+
108
+ Use `waitForResult` to wait for the full pipeline to complete. The SDK opens an SSE stream behind the scenes and returns the result with all pipeline steps. **This blocks until the pipeline finishes** (~120s max).
109
+
110
+ ```ts
111
+ const result = await hw.send(
112
+ { event: 'order.created', orderId: '123' },
113
+ { waitForResult: true },
114
+ );
115
+
116
+ console.log(result.syncStatus); // 200 = delivered, 422 = validation failed, 502 = failed, 408 = timeout
117
+ console.log(result.steps); // array of pipeline steps with nodeType, status, durationMs
118
+ console.log(result.response); // response body from target URL
119
+ ```
120
+
121
+ ### Non-blocking result (recommended for UIs)
122
+
123
+ Use `onResult` to get the pipeline result in a background callback. `send()` returns immediately so your UI never blocks. Perfect for forms and user-facing apps.
124
+
125
+ ```ts
126
+ const { eventId } = await hw.send(
127
+ { event: 'order.created', orderId: '123' },
128
+ {
129
+ onResult: (result) => {
130
+ // Fires when the pipeline completes (via SSE in background)
131
+ if (result.awaitingApproval) {
132
+ showToast('Awaiting approval from ' + result.approvalNodeName);
133
+ } else if (result.syncStatus === 200) {
134
+ showToast('Delivered successfully');
135
+ } else {
136
+ showToast('Pipeline failed: ' + result.error);
137
+ }
138
+ },
139
+ onStep: (step) => {
140
+ // Optional: fires for each pipeline node as it executes
141
+ updateProgress(step.nodeName, step.status);
142
+ },
143
+ },
144
+ );
145
+ // UI shows "Sent!" immediately here — never blocks
146
+ ```
147
+
148
+ > **Important:** `onResult` requires the endpoint to be in **None** response mode (async). In Sync mode, the server holds the HTTP connection open until the pipeline completes, which blocks `send()` regardless of callbacks. See [Choosing the right mode](#choosing-the-right-mode) below.
149
+
150
+ ### Deduplication detection
151
+
152
+ When an event is deduplicated, the response includes `deduplicated: true`:
153
+
154
+ ```ts
155
+ const result = await hw.send({ event: 'order.created', orderId: '123' });
156
+
157
+ if (result.deduplicated) {
158
+ console.log('Duplicate! Original event:', result.originalEventId);
159
+ }
160
+ ```
161
+
162
+ ### Batch
163
+
164
+ ```ts
165
+ const results = await hw.sendBatch([
166
+ { event: 'user.signup', userId: '1' },
167
+ { event: 'user.signup', userId: '2' },
168
+ ]);
169
+ ```
170
+
171
+ ### Standalone Function
172
+
173
+ ```ts
174
+ import { send } from '@hostwebhook/node';
175
+
176
+ const { eventId } = await send(
177
+ { token: 'your_ingress_token' },
178
+ { event: 'test', data: 'hello' },
179
+ );
180
+ ```
181
+
182
+ ## Choosing the right mode
183
+
184
+ Your endpoint has a **Response Mode** setting (None or Sync) that affects how `send()` behaves:
185
+
186
+ | Scenario | Endpoint mode | SDK option | Behavior |
187
+ |----------|--------------|------------|----------|
188
+ | **Forms / UIs** | None | `onResult` | `send()` returns instantly. Pipeline result arrives in background callback. **UI never blocks.** |
189
+ | **Backend scripts** | None | `waitForResult` | `send()` blocks until pipeline completes via SSE (~120s max). |
190
+ | **API proxy / Stripe** | Sync | *(none)* | `send()` blocks until pipeline completes via HTTP (~120s max). Result is inline in the response. |
191
+ | **Testing (Postman)** | Sync | *(none)* | Single request, response includes full pipeline result. |
192
+
193
+ > **Warning: Never use Sync mode for user-facing forms or UIs.** Sync mode holds the HTTP connection open on the server, which blocks `send()` and freezes the UI until the pipeline completes. If the pipeline has approval nodes, merge nodes, or long delays, the SDK will timeout. Use **None** mode with `onResult` instead — the user sees "Sent!" immediately and the pipeline result arrives in the background.
194
+
195
+ ### When to use Sync mode
196
+
197
+ - The caller is a **server or script**, not a human looking at a UI
198
+ - The caller **needs the pipeline result in the same HTTP request** (e.g., Stripe expects a specific response body)
199
+ - The pipeline is **fast and linear** (filter -> transform -> action) with no human-gated nodes (approval, merge)
200
+
201
+ ### When to use None mode + onResult
202
+
203
+ - The caller is a **form, UI, or frontend app**
204
+ - You want **instant feedback** ("Sent!") without waiting for the pipeline
205
+ - The pipeline has **approval nodes, merge nodes, or delays**
206
+ - You want **real-time progress** via `onStep` callbacks
207
+
208
+ ## Configuration
209
+
210
+ | Option | Type | Default | Description |
211
+ |--------|------|---------|-------------|
212
+ | `token` | `string` | *required* | Endpoint ingress token |
213
+ | `baseUrl` | `string` | `https://api.hostwebhook.com` | API base URL |
214
+ | `signingSecret` | `string` | — | Signs outgoing requests with HMAC-SHA256 |
215
+ | `signature` | `StaticSignature` | — | Static secret signature (alternative to HMAC) |
216
+ | `defaultHeaders` | `Record<string, string>` | — | Headers added to every request |
217
+ | `timeoutMs` | `number` | `10000` | Request timeout in ms |
218
+
219
+ ### SendOptions
220
+
221
+ | Option | Type | Default | Description |
222
+ |--------|------|---------|-------------|
223
+ | `headers` | `Record<string, string>` | — | Per-request headers |
224
+ | `contentType` | `string` | `application/json` | Override content type |
225
+ | `rawBody` | `string \| Buffer` | — | Send raw body instead of JSON |
226
+ | `waitForResult` | `boolean` | `false` | Wait for pipeline result via SSE stream (blocking) |
227
+ | `onResult` | `(result) => void` | — | Non-blocking pipeline result callback via SSE |
228
+ | `onStep` | `(step) => void` | — | Real-time pipeline step callback (with `onResult` or `waitForResult`) |
229
+
230
+ ## Error Handling
231
+
232
+ ```ts
233
+ import { SendError, VerificationError } from '@hostwebhook/node';
234
+
235
+ // Send errors
236
+ try {
237
+ await hw.send(payload);
238
+ } catch (err) {
239
+ if (err instanceof SendError) {
240
+ console.log(err.statusCode, err.responseBody);
241
+ }
242
+ }
243
+
244
+ // Verification errors
245
+ try {
246
+ verify(body, headers, secret);
247
+ } catch (err) {
248
+ if (err instanceof VerificationError) {
249
+ console.log(err.message);
250
+ }
251
+ }
252
+ ```
253
+
254
+ ## Signature Format
255
+
256
+ ```
257
+ X-HostWebhook-Signature: t=<timestamp_ms>,v1=<hmac_hex>
258
+ ```
259
+
260
+ HMAC computed as: `HMAC-SHA256(secret, "${timestamp}.${rawBody}")`
261
+
262
+ ## Support
263
+
264
+ Found a bug or have a feature request? [Report an issue](https://www.hostwebhook.com/#issues) on our website.
265
+
266
+ ## Privacy
267
+
268
+ Your webhook data is yours. HostWebhook is built with privacy as a core principle:
269
+
270
+ - **Your payloads stay between you and your endpoints** — we never sell, share, or use your data for training.
271
+ - **Encryption in transit** — all traffic between the SDK, the platform, and your endpoints is sent over TLS.
272
+ - **Minimal data retention** — event payloads are stored only for delivery and debugging, and are automatically purged based on your plan's retention window.
273
+
274
+ ## License
275
+
276
+ MIT
package/dist/index.d.mts CHANGED
@@ -192,7 +192,7 @@ interface HostWebhookConfig {
192
192
  apiKey?: string;
193
193
  /**
194
194
  * Default ingress token. When set, `send(payload)` uses this token.
195
- * For multi-endpoint usage, pass the token per-call: `send(token, payload)`.
195
+ * For multi-webhook usage, pass the token per-call: `send(token, payload)`.
196
196
  */
197
197
  token?: string;
198
198
  /** Base URL of the HostWebhook API. Default: `https://api.hostwebhook.com` */
@@ -200,7 +200,7 @@ interface HostWebhookConfig {
200
200
  /**
201
201
  * HMAC-SHA256 signing secret. When set, every request is signed with the
202
202
  * HostWebhook signature format (`X-HostWebhook-Signature: t=<ts>,v1=<hmac>`).
203
- * Pair with `incomingSignatureType: 'hostwebhook'` on the endpoint.
203
+ * Pair with `incomingSignatureType: 'hostwebhook'` on the webhook.
204
204
  * @deprecated Prefer `apiKey` for authentication.
205
205
  */
206
206
  signingSecret?: string;
@@ -262,7 +262,7 @@ interface SendOptions {
262
262
  * it does NOT wait for the approval to be resolved (that can take hours/days)
263
263
  * - Delay nodes — if the delay exceeds the 125s timeout, the stream times out
264
264
  *
265
- * For approval workflows, use `responseMode: 'sync'` on the endpoint instead —
265
+ * For approval workflows, use `responseMode: 'sync'` on the webhook instead —
266
266
  * it returns the approval status inline without blocking the SSE stream.
267
267
  */
268
268
  waitForResult?: boolean;
@@ -324,13 +324,13 @@ declare class SendError extends Error {
324
324
  /**
325
325
  * HostWebhook client for sending webhooks through the platform.
326
326
  *
327
- * @example Single-endpoint
327
+ * @example Single-webhook
328
328
  * ```ts
329
329
  * const hw = new HostWebhook({ apiKey: 'hwk_...', token: 'ingress_token' });
330
330
  * await hw.send({ event: 'order.created' });
331
331
  * ```
332
332
  *
333
- * @example Multi-endpoint
333
+ * @example Multi-webhook
334
334
  * ```ts
335
335
  * const hw = new HostWebhook({ apiKey: 'hwk_...' });
336
336
  * await hw.send('token_a', { event: 'order.created' });
package/dist/index.d.ts CHANGED
@@ -192,7 +192,7 @@ interface HostWebhookConfig {
192
192
  apiKey?: string;
193
193
  /**
194
194
  * Default ingress token. When set, `send(payload)` uses this token.
195
- * For multi-endpoint usage, pass the token per-call: `send(token, payload)`.
195
+ * For multi-webhook usage, pass the token per-call: `send(token, payload)`.
196
196
  */
197
197
  token?: string;
198
198
  /** Base URL of the HostWebhook API. Default: `https://api.hostwebhook.com` */
@@ -200,7 +200,7 @@ interface HostWebhookConfig {
200
200
  /**
201
201
  * HMAC-SHA256 signing secret. When set, every request is signed with the
202
202
  * HostWebhook signature format (`X-HostWebhook-Signature: t=<ts>,v1=<hmac>`).
203
- * Pair with `incomingSignatureType: 'hostwebhook'` on the endpoint.
203
+ * Pair with `incomingSignatureType: 'hostwebhook'` on the webhook.
204
204
  * @deprecated Prefer `apiKey` for authentication.
205
205
  */
206
206
  signingSecret?: string;
@@ -262,7 +262,7 @@ interface SendOptions {
262
262
  * it does NOT wait for the approval to be resolved (that can take hours/days)
263
263
  * - Delay nodes — if the delay exceeds the 125s timeout, the stream times out
264
264
  *
265
- * For approval workflows, use `responseMode: 'sync'` on the endpoint instead —
265
+ * For approval workflows, use `responseMode: 'sync'` on the webhook instead —
266
266
  * it returns the approval status inline without blocking the SSE stream.
267
267
  */
268
268
  waitForResult?: boolean;
@@ -324,13 +324,13 @@ declare class SendError extends Error {
324
324
  /**
325
325
  * HostWebhook client for sending webhooks through the platform.
326
326
  *
327
- * @example Single-endpoint
327
+ * @example Single-webhook
328
328
  * ```ts
329
329
  * const hw = new HostWebhook({ apiKey: 'hwk_...', token: 'ingress_token' });
330
330
  * await hw.send({ event: 'order.created' });
331
331
  * ```
332
332
  *
333
- * @example Multi-endpoint
333
+ * @example Multi-webhook
334
334
  * ```ts
335
335
  * const hw = new HostWebhook({ apiKey: 'hwk_...' });
336
336
  * await hw.send('token_a', { event: 'order.created' });
package/dist/index.js CHANGED
@@ -410,7 +410,7 @@ doSend_fn = async function(token, payload, options) {
410
410
  }
411
411
  const headers = {
412
412
  "Content-Type": options?.contentType ?? "application/json",
413
- "User-Agent": `HostWebhook-SDK/${"1.5.1"}`,
413
+ "User-Agent": `HostWebhook-SDK/${"1.6.1"}`,
414
414
  "X-HostWebhook-Sent-At": (/* @__PURE__ */ new Date()).toISOString(),
415
415
  ...tz ? { "X-HostWebhook-Timezone": tz } : {},
416
416
  ...__privateGet(this, _defaultHeaders),
package/dist/index.mjs CHANGED
@@ -376,7 +376,7 @@ doSend_fn = async function(token, payload, options) {
376
376
  }
377
377
  const headers = {
378
378
  "Content-Type": options?.contentType ?? "application/json",
379
- "User-Agent": `HostWebhook-SDK/${"1.5.1"}`,
379
+ "User-Agent": `HostWebhook-SDK/${"1.6.1"}`,
380
380
  "X-HostWebhook-Sent-At": (/* @__PURE__ */ new Date()).toISOString(),
381
381
  ...tz ? { "X-HostWebhook-Timezone": tz } : {},
382
382
  ...__privateGet(this, _defaultHeaders),
package/llms.txt CHANGED
@@ -1,167 +1,167 @@
1
- # @hostwebhook/node
2
-
3
- > Official HostWebhook Node.js SDK — verify webhook signatures and send webhooks through the platform.
4
-
5
- ## Install
6
-
7
- npm install @hostwebhook/node
8
-
9
- ## Verify Signatures (receiving webhooks)
10
-
11
- HostWebhook signs every delivery with HMAC-SHA256. Use these helpers to verify authenticity.
12
-
13
- ### Express
14
-
15
- ```ts
16
- import express from 'express';
17
- import { expressMiddleware } from '@hostwebhook/node';
18
-
19
- const app = express();
20
- app.post('/webhooks',
21
- express.raw({ type: 'application/json' }),
22
- expressMiddleware(process.env.SIGNING_SECRET!),
23
- (req, res) => {
24
- const payload = JSON.parse(req.body.toString());
25
- // process payload...
26
- res.json({ received: true });
27
- },
28
- );
29
- ```
30
-
31
- IMPORTANT: Use `express.raw()` so `req.body` is a Buffer, not parsed JSON.
32
-
33
- ### NestJS
34
-
35
- ```ts
36
- import { Controller, Post, UseGuards, Req } from '@nestjs/common';
37
- import { createNestGuard } from '@hostwebhook/node';
38
-
39
- const WebhookGuard = createNestGuard(process.env.SIGNING_SECRET!);
40
-
41
- @Controller('webhooks')
42
- export class WebhooksController {
43
- @Post()
44
- @UseGuards(WebhookGuard)
45
- handle(@Req() req: Request) {
46
- return { received: true };
47
- }
48
- }
49
- ```
50
-
51
- ### Fastify
52
-
53
- ```ts
54
- import Fastify from 'fastify';
55
- import { fastifyHook } from '@hostwebhook/node';
56
-
57
- const app = Fastify();
58
- app.addContentTypeParser('application/json', { parseAs: 'buffer' }, (req, body, done) => done(null, body));
59
-
60
- app.post('/webhooks', {
61
- preHandler: fastifyHook(process.env.SIGNING_SECRET!),
62
- }, (req, reply) => {
63
- reply.send({ received: true });
64
- });
65
- ```
66
-
67
- ### Manual verification
68
-
69
- ```ts
70
- import { verify, VerificationError } from '@hostwebhook/node';
71
-
72
- try {
73
- verify(rawBody, request.headers, secret);
74
- // signature is valid
75
- } catch (err) {
76
- if (err instanceof VerificationError) {
77
- // invalid signature — reject
78
- }
79
- }
80
- ```
81
-
82
- verify(payload, headers, secret, options?) — throws VerificationError if invalid.
83
- VerifyOptions: { maxAgeSec?: number } — default 300 (5 min). Set 0 to disable replay protection.
84
-
85
- ## Send Webhooks (sending through HostWebhook)
86
-
87
- ### Class-based client
88
-
89
- ```ts
90
- import { HostWebhook } from '@hostwebhook/node';
91
-
92
- const hw = new HostWebhook({ token: 'your_ingress_token' });
93
-
94
- // Send a single event
95
- const { eventId } = await hw.send({ event: 'order.created', orderId: '123' });
96
-
97
- // Send multiple events in parallel
98
- const results = await hw.sendBatch([
99
- { event: 'user.signup', userId: '1' },
100
- { event: 'user.signup', userId: '2' },
101
- ]);
102
- ```
103
-
104
- ### Standalone function (no class needed)
105
-
106
- ```ts
107
- import { send } from '@hostwebhook/node';
108
-
109
- const { eventId } = await send(
110
- { token: 'your_ingress_token' },
111
- { event: 'test', data: 'hello' },
112
- );
113
- ```
114
-
115
- ## Configuration
116
-
117
- HostWebhookConfig: {
118
- token: string (required — your endpoint's ingress token),
119
- baseUrl?: string (default: https://api.hostwebhook.com),
120
- signingSecret?: string (optional — signs outgoing requests with HMAC-SHA256),
121
- defaultHeaders?: Record<string, string>,
122
- timeoutMs?: number (default: 10000),
123
- }
124
-
125
- SendOptions: {
126
- headers?: Record<string, string>,
127
- contentType?: string (default: application/json),
128
- rawBody?: string | Buffer (skip JSON.stringify, send raw),
129
- }
130
-
131
- SendResult: { eventId: string }
132
-
133
- ## Signature Format
134
-
135
- Header: `X-HostWebhook-Signature: t=<timestamp_ms>,v1=<hmac_hex>`
136
- HMAC computed as: `HMAC-SHA256(secret, "${timestamp}.${rawBody}")`
137
-
138
- ## Error Handling
139
-
140
- ```ts
141
- import { SendError, VerificationError } from '@hostwebhook/node';
142
-
143
- // SendError — thrown when API returns non-2xx
144
- try {
145
- await hw.send(payload);
146
- } catch (err) {
147
- if (err instanceof SendError) {
148
- console.log(err.statusCode, err.responseBody);
149
- }
150
- }
151
-
152
- // VerificationError — thrown when signature is invalid
153
- try {
154
- verify(body, headers, secret);
155
- } catch (err) {
156
- if (err instanceof VerificationError) {
157
- console.log(err.message); // e.g. "Signature mismatch"
158
- }
159
- }
160
- ```
161
-
162
- ## Exports
163
-
164
- Functions: verify, expressMiddleware, createNestGuard, fastifyHook, send
165
- Classes: HostWebhook, SendError, VerificationError
166
- Types: VerifyOptions, HostWebhookConfig, SendOptions, SendResult, WebhookHeaders
167
- Helpers: parseHeaders (extract webhook metadata from headers)
1
+ # @hostwebhook/node
2
+
3
+ > Official HostWebhook Node.js SDK — verify webhook signatures and send webhooks through the platform.
4
+
5
+ ## Install
6
+
7
+ npm install @hostwebhook/node
8
+
9
+ ## Verify Signatures (receiving webhooks)
10
+
11
+ HostWebhook signs every delivery with HMAC-SHA256. Use these helpers to verify authenticity.
12
+
13
+ ### Express
14
+
15
+ ```ts
16
+ import express from 'express';
17
+ import { expressMiddleware } from '@hostwebhook/node';
18
+
19
+ const app = express();
20
+ app.post('/webhooks',
21
+ express.raw({ type: 'application/json' }),
22
+ expressMiddleware(process.env.SIGNING_SECRET!),
23
+ (req, res) => {
24
+ const payload = JSON.parse(req.body.toString());
25
+ // process payload...
26
+ res.json({ received: true });
27
+ },
28
+ );
29
+ ```
30
+
31
+ IMPORTANT: Use `express.raw()` so `req.body` is a Buffer, not parsed JSON.
32
+
33
+ ### NestJS
34
+
35
+ ```ts
36
+ import { Controller, Post, UseGuards, Req } from '@nestjs/common';
37
+ import { createNestGuard } from '@hostwebhook/node';
38
+
39
+ const WebhookGuard = createNestGuard(process.env.SIGNING_SECRET!);
40
+
41
+ @Controller('webhooks')
42
+ export class WebhooksController {
43
+ @Post()
44
+ @UseGuards(WebhookGuard)
45
+ handle(@Req() req: Request) {
46
+ return { received: true };
47
+ }
48
+ }
49
+ ```
50
+
51
+ ### Fastify
52
+
53
+ ```ts
54
+ import Fastify from 'fastify';
55
+ import { fastifyHook } from '@hostwebhook/node';
56
+
57
+ const app = Fastify();
58
+ app.addContentTypeParser('application/json', { parseAs: 'buffer' }, (req, body, done) => done(null, body));
59
+
60
+ app.post('/webhooks', {
61
+ preHandler: fastifyHook(process.env.SIGNING_SECRET!),
62
+ }, (req, reply) => {
63
+ reply.send({ received: true });
64
+ });
65
+ ```
66
+
67
+ ### Manual verification
68
+
69
+ ```ts
70
+ import { verify, VerificationError } from '@hostwebhook/node';
71
+
72
+ try {
73
+ verify(rawBody, request.headers, secret);
74
+ // signature is valid
75
+ } catch (err) {
76
+ if (err instanceof VerificationError) {
77
+ // invalid signature — reject
78
+ }
79
+ }
80
+ ```
81
+
82
+ verify(payload, headers, secret, options?) — throws VerificationError if invalid.
83
+ VerifyOptions: { maxAgeSec?: number } — default 300 (5 min). Set 0 to disable replay protection.
84
+
85
+ ## Send Webhooks (sending through HostWebhook)
86
+
87
+ ### Class-based client
88
+
89
+ ```ts
90
+ import { HostWebhook } from '@hostwebhook/node';
91
+
92
+ const hw = new HostWebhook({ token: 'your_ingress_token' });
93
+
94
+ // Send a single event
95
+ const { eventId } = await hw.send({ event: 'order.created', orderId: '123' });
96
+
97
+ // Send multiple events in parallel
98
+ const results = await hw.sendBatch([
99
+ { event: 'user.signup', userId: '1' },
100
+ { event: 'user.signup', userId: '2' },
101
+ ]);
102
+ ```
103
+
104
+ ### Standalone function (no class needed)
105
+
106
+ ```ts
107
+ import { send } from '@hostwebhook/node';
108
+
109
+ const { eventId } = await send(
110
+ { token: 'your_ingress_token' },
111
+ { event: 'test', data: 'hello' },
112
+ );
113
+ ```
114
+
115
+ ## Configuration
116
+
117
+ HostWebhookConfig: {
118
+ token: string (required — your endpoint's ingress token),
119
+ baseUrl?: string (default: https://api.hostwebhook.com),
120
+ signingSecret?: string (optional — signs outgoing requests with HMAC-SHA256),
121
+ defaultHeaders?: Record<string, string>,
122
+ timeoutMs?: number (default: 10000),
123
+ }
124
+
125
+ SendOptions: {
126
+ headers?: Record<string, string>,
127
+ contentType?: string (default: application/json),
128
+ rawBody?: string | Buffer (skip JSON.stringify, send raw),
129
+ }
130
+
131
+ SendResult: { eventId: string }
132
+
133
+ ## Signature Format
134
+
135
+ Header: `X-HostWebhook-Signature: t=<timestamp_ms>,v1=<hmac_hex>`
136
+ HMAC computed as: `HMAC-SHA256(secret, "${timestamp}.${rawBody}")`
137
+
138
+ ## Error Handling
139
+
140
+ ```ts
141
+ import { SendError, VerificationError } from '@hostwebhook/node';
142
+
143
+ // SendError — thrown when API returns non-2xx
144
+ try {
145
+ await hw.send(payload);
146
+ } catch (err) {
147
+ if (err instanceof SendError) {
148
+ console.log(err.statusCode, err.responseBody);
149
+ }
150
+ }
151
+
152
+ // VerificationError — thrown when signature is invalid
153
+ try {
154
+ verify(body, headers, secret);
155
+ } catch (err) {
156
+ if (err instanceof VerificationError) {
157
+ console.log(err.message); // e.g. "Signature mismatch"
158
+ }
159
+ }
160
+ ```
161
+
162
+ ## Exports
163
+
164
+ Functions: verify, expressMiddleware, createNestGuard, fastifyHook, send
165
+ Classes: HostWebhook, SendError, VerificationError
166
+ Types: VerifyOptions, HostWebhookConfig, SendOptions, SendResult, WebhookHeaders
167
+ Helpers: parseHeaders (extract webhook metadata from headers)
package/package.json CHANGED
@@ -1,43 +1,43 @@
1
- {
2
- "name": "@hostwebhook/node",
3
- "version": "1.6.0",
4
- "description": "Official HostWebhook SDK — verify signatures, parse headers, and send webhooks",
5
- "main": "dist/index.js",
6
- "module": "dist/index.mjs",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.mjs",
12
- "require": "./dist/index.js"
13
- }
14
- },
15
- "files": [
16
- "dist",
17
- "README.md",
18
- "llms.txt"
19
- ],
20
- "scripts": {
21
- "build": "tsup",
22
- "test": "node --test __tests__/sdk.test.mjs",
23
- "prepublishOnly": "npm run build"
24
- },
25
- "keywords": [
26
- "hostwebhook",
27
- "webhook",
28
- "signature",
29
- "verify",
30
- "send",
31
- "hmac",
32
- "retry"
33
- ],
34
- "license": "MIT",
35
- "devDependencies": {
36
- "@types/node": "^25.3.3",
37
- "tsup": "^8.0.0",
38
- "typescript": "^5.3.0"
39
- },
40
- "engines": {
41
- "node": ">=18"
42
- }
43
- }
1
+ {
2
+ "name": "@hostwebhook/node",
3
+ "version": "1.6.1",
4
+ "description": "Official HostWebhook SDK — verify signatures, parse headers, and send webhooks",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "llms.txt"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "test": "node --test __tests__/sdk.test.mjs",
23
+ "prepublishOnly": "npm run build"
24
+ },
25
+ "keywords": [
26
+ "hostwebhook",
27
+ "webhook",
28
+ "signature",
29
+ "verify",
30
+ "send",
31
+ "hmac",
32
+ "retry"
33
+ ],
34
+ "license": "MIT",
35
+ "devDependencies": {
36
+ "@types/node": "^25.3.3",
37
+ "tsup": "^8.0.0",
38
+ "typescript": "^5.3.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=18"
42
+ }
43
+ }