@neo-edi/sdk 1.0.20 → 1.0.21
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 +88 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -267,6 +267,94 @@ const data = await client.graphql.query<{ edi852Headers: Edi852Header[] }>(`
|
|
|
267
267
|
`, { filter: { vendorPartnerId: 'VENDOR-001' } });
|
|
268
268
|
```
|
|
269
269
|
|
|
270
|
+
## Webhooks
|
|
271
|
+
|
|
272
|
+
Register endpoints that receive HMAC-signed HTTP callbacks when events happen for a
|
|
273
|
+
trading partner — e.g. `document.ingested` when a delivery is ingested — so you don't
|
|
274
|
+
have to poll.
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// Create (subscribe) an endpoint.
|
|
278
|
+
// The returned signingSecret is shown ONCE — store it now.
|
|
279
|
+
const endpoint = await client.webhooks.create({
|
|
280
|
+
partnerId: 'PARTNER-001',
|
|
281
|
+
url: 'https://your-app.com/hooks/neo-edi',
|
|
282
|
+
description: 'Production receiver',
|
|
283
|
+
eventTypes: ['document.ingested'] // omit to subscribe to all events
|
|
284
|
+
});
|
|
285
|
+
console.log(endpoint.signingSecret); // whsec_... — persist this securely
|
|
286
|
+
|
|
287
|
+
// List endpoints for a partner
|
|
288
|
+
const endpoints = await client.webhooks.list('PARTNER-001');
|
|
289
|
+
|
|
290
|
+
// Get one endpoint
|
|
291
|
+
const one = await client.webhooks.get(endpoint.id);
|
|
292
|
+
|
|
293
|
+
// Update — change the URL, events, or pause delivery with isActive: false
|
|
294
|
+
await client.webhooks.update(endpoint.id, {
|
|
295
|
+
url: 'https://your-app.com/hooks/neo-edi/v2',
|
|
296
|
+
isActive: false
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// Delete (unsubscribe)
|
|
300
|
+
await client.webhooks.delete(endpoint.id);
|
|
301
|
+
|
|
302
|
+
// Inspect recent delivery attempts (audit / debugging)
|
|
303
|
+
const deliveries = await client.webhooks.listDeliveries(endpoint.id, { limit: 20 });
|
|
304
|
+
for (const d of deliveries) {
|
|
305
|
+
console.log(d.status, d.responseStatus, d.attemptCount);
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Receiving & verifying deliveries
|
|
310
|
+
|
|
311
|
+
Each delivery is an HTTP `POST` to your URL with a JSON envelope
|
|
312
|
+
(`{ id, type, partnerId, createdAt, data }`) and these headers:
|
|
313
|
+
|
|
314
|
+
| Header | Example | Purpose |
|
|
315
|
+
|--------|---------|---------|
|
|
316
|
+
| `X-Webhook-Timestamp` | `1717416000` | Unix seconds; part of the signed content |
|
|
317
|
+
| `X-Webhook-Signature` | `t=1717416000,v1=9f86d08...` | `v1` is the HMAC-SHA256 hex digest |
|
|
318
|
+
|
|
319
|
+
Verify by recomputing `HMAC_SHA256(signingSecret, "{timestamp}.{rawBody}")` over the
|
|
320
|
+
**raw** body (before JSON parsing) and comparing it to the `v1=` value. Example with
|
|
321
|
+
Express:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import crypto from 'node:crypto';
|
|
325
|
+
import express from 'express';
|
|
326
|
+
|
|
327
|
+
const app = express();
|
|
328
|
+
|
|
329
|
+
// Capture the raw body — the signature is computed over the exact bytes sent.
|
|
330
|
+
app.post(
|
|
331
|
+
'/hooks/neo-edi',
|
|
332
|
+
express.raw({ type: 'application/json' }),
|
|
333
|
+
(req, res) => {
|
|
334
|
+
const sig = req.header('X-Webhook-Signature') ?? '';
|
|
335
|
+
const parts = Object.fromEntries(sig.split(',').map((kv) => kv.split('=')));
|
|
336
|
+
const rawBody = req.body.toString('utf8');
|
|
337
|
+
|
|
338
|
+
const expected = crypto
|
|
339
|
+
.createHmac('sha256', process.env.WEBHOOK_SECRET!)
|
|
340
|
+
.update(`${parts.t}.${rawBody}`)
|
|
341
|
+
.digest('hex');
|
|
342
|
+
|
|
343
|
+
const valid =
|
|
344
|
+
parts.v1?.length === expected.length &&
|
|
345
|
+
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
|
|
346
|
+
|
|
347
|
+
if (!valid) return res.status(401).send('bad signature');
|
|
348
|
+
|
|
349
|
+
const event = JSON.parse(rawBody);
|
|
350
|
+
// Dedupe on event.id — it is stable across retries.
|
|
351
|
+
// ... handle event.data ...
|
|
352
|
+
|
|
353
|
+
res.sendStatus(200); // any 2xx acknowledges; otherwise it's retried (up to 3x)
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
```
|
|
357
|
+
|
|
270
358
|
## Error Handling
|
|
271
359
|
|
|
272
360
|
```typescript
|
package/dist/index.js
CHANGED
|
@@ -63,7 +63,7 @@ var NeoEdiValidationError = class extends NeoEdiError {
|
|
|
63
63
|
// package.json
|
|
64
64
|
var package_default = {
|
|
65
65
|
name: "@neo-edi/sdk",
|
|
66
|
-
version: "1.0.
|
|
66
|
+
version: "1.0.21",
|
|
67
67
|
description: "TypeScript SDK for the Neo-EDI platform",
|
|
68
68
|
main: "./dist/index.js",
|
|
69
69
|
module: "./dist/index.mjs",
|
package/dist/index.mjs
CHANGED
|
@@ -32,7 +32,7 @@ var NeoEdiValidationError = class extends NeoEdiError {
|
|
|
32
32
|
// package.json
|
|
33
33
|
var package_default = {
|
|
34
34
|
name: "@neo-edi/sdk",
|
|
35
|
-
version: "1.0.
|
|
35
|
+
version: "1.0.21",
|
|
36
36
|
description: "TypeScript SDK for the Neo-EDI platform",
|
|
37
37
|
main: "./dist/index.js",
|
|
38
38
|
module: "./dist/index.mjs",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neo-edi/sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
4
4
|
"description": "TypeScript SDK for the Neo-EDI platform",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@neo-edi/types": "1.0.
|
|
19
|
+
"@neo-edi/types": "1.0.21"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "^20",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"typescript": "^5.0.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"@neo-edi/types": "1.0.
|
|
27
|
+
"@neo-edi/types": "1.0.21"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|