@webhooks-cc/sdk 0.6.0 → 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/README.md +316 -140
- package/dist/chunk-7IMPSHQY.mjs +236 -0
- package/dist/diff-Dn4j4B_n.d.mts +701 -0
- package/dist/diff-Dn4j4B_n.d.ts +701 -0
- package/dist/index.d.mts +137 -340
- package/dist/index.d.ts +137 -340
- package/dist/index.js +1776 -168
- package/dist/index.mjs +1661 -291
- package/dist/testing.d.mts +30 -0
- package/dist/testing.d.ts +30 -0
- package/dist/testing.js +360 -0
- package/dist/testing.mjs +124 -0
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1,250 +1,426 @@
|
|
|
1
1
|
# @webhooks-cc/sdk
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for [webhooks.cc](https://webhooks.cc). Create webhook endpoints, capture requests,
|
|
3
|
+
TypeScript SDK for [webhooks.cc](https://webhooks.cc). Create webhook endpoints, capture and search requests, send signed test webhooks, verify provider signatures, and build webhook tests with less boilerplate.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
pnpm add @webhooks-cc/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The package also ships a testing entrypoint:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { captureDuring, assertRequest } from "@webhooks-cc/sdk/testing";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## API key setup
|
|
18
|
+
|
|
19
|
+
The SDK needs an API key in `whcc_...` format. You can pass the key directly, but most projects load it from `WHK_API_KEY` so the same code works locally and in CI.
|
|
20
|
+
|
|
21
|
+
For local development, set the env var in your shell or `.env.local`:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
export WHK_API_KEY=whcc_...
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For GitHub Actions, store the key as a repository secret and expose it in the workflow:
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
# .github/workflows/test.yml
|
|
31
|
+
env:
|
|
32
|
+
WHK_API_KEY: ${{ secrets.WHK_API_KEY }}
|
|
10
33
|
```
|
|
11
34
|
|
|
12
35
|
## Quick start
|
|
13
36
|
|
|
14
37
|
```typescript
|
|
15
|
-
import { WebhooksCC, matchAll,
|
|
38
|
+
import { WebhooksCC, matchAll, matchHeader, matchMethod } from "@webhooks-cc/sdk";
|
|
16
39
|
|
|
17
|
-
const client = new WebhooksCC({ apiKey:
|
|
40
|
+
const client = new WebhooksCC({ apiKey: process.env.WHK_API_KEY! });
|
|
41
|
+
|
|
42
|
+
const endpoint = await client.endpoints.create({
|
|
43
|
+
name: "stripe-test",
|
|
44
|
+
expiresIn: "1h",
|
|
45
|
+
});
|
|
18
46
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
console.log(endpoint.url); // https://go.webhooks.cc/w/abc123
|
|
47
|
+
await yourApp.registerWebhook(endpoint.url!);
|
|
48
|
+
await yourApp.triggerCheckout();
|
|
22
49
|
|
|
23
|
-
// Point your service at endpoint.url, then wait for the webhook
|
|
24
50
|
const request = await client.requests.waitFor(endpoint.slug, {
|
|
25
51
|
timeout: "30s",
|
|
26
52
|
match: matchAll(matchMethod("POST"), matchHeader("stripe-signature")),
|
|
27
53
|
});
|
|
28
54
|
|
|
29
|
-
console.log(request.body);
|
|
55
|
+
console.log(request.body);
|
|
30
56
|
|
|
31
|
-
// Clean up
|
|
32
57
|
await client.endpoints.delete(endpoint.slug);
|
|
33
58
|
```
|
|
34
59
|
|
|
35
60
|
## Client options
|
|
36
61
|
|
|
37
|
-
```typescript
|
|
38
|
-
new WebhooksCC(options);
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
| Option | Type | Default | Description |
|
|
42
|
-
| ------------ | ------------- | ------------------------ | ------------------------- |
|
|
43
|
-
| `apiKey` | `string` | _required_ | API key (`whcc_...`) |
|
|
44
|
-
| `baseUrl` | `string` | `https://webhooks.cc` | API base URL |
|
|
45
|
-
| `webhookUrl` | `string` | `https://go.webhooks.cc` | Webhook receiver URL |
|
|
46
|
-
| `timeout` | `number` | `30000` | HTTP request timeout (ms) |
|
|
47
|
-
| `hooks` | `ClientHooks` | — | Lifecycle callbacks |
|
|
48
|
-
|
|
49
|
-
### Hooks
|
|
50
|
-
|
|
51
62
|
```typescript
|
|
52
63
|
const client = new WebhooksCC({
|
|
53
64
|
apiKey: "whcc_...",
|
|
65
|
+
retry: {
|
|
66
|
+
maxAttempts: 3,
|
|
67
|
+
backoffMs: 500,
|
|
68
|
+
},
|
|
54
69
|
hooks: {
|
|
55
|
-
onRequest: (
|
|
56
|
-
onResponse: (
|
|
57
|
-
onError: (
|
|
70
|
+
onRequest: ({ method, url }) => console.log(method, url),
|
|
71
|
+
onResponse: ({ status, durationMs }) => console.log(status, durationMs),
|
|
72
|
+
onError: ({ error }) => console.error(error),
|
|
58
73
|
},
|
|
59
74
|
});
|
|
60
75
|
```
|
|
61
76
|
|
|
77
|
+
| Option | Type | Default | Notes |
|
|
78
|
+
| ------------ | -------------- | ------------------------ | ------------------------------------------------------------------------ |
|
|
79
|
+
| `apiKey` | `string` | required | API key in `whcc_...` format. Often read from `process.env.WHK_API_KEY`. |
|
|
80
|
+
| `baseUrl` | `string` | `https://webhooks.cc` | API base URL |
|
|
81
|
+
| `webhookUrl` | `string` | `https://go.webhooks.cc` | receiver base URL used by `endpoints.send()` |
|
|
82
|
+
| `timeout` | `number` | `30000` | request timeout in milliseconds |
|
|
83
|
+
| `retry` | `RetryOptions` | `1` attempt | retries transient SDK requests |
|
|
84
|
+
| `hooks` | `ClientHooks` | none | lifecycle callbacks for request logging |
|
|
85
|
+
|
|
86
|
+
## API overview
|
|
87
|
+
|
|
88
|
+
- `client.endpoints`: `create`, `list`, `get`, `update`, `delete`, `send`, `sendTemplate`
|
|
89
|
+
- `client.requests`: `list`, `listPaginated`, `get`, `waitFor`, `waitForAll`, `subscribe`, `replay`, `search`, `count`, `clear`, `export`
|
|
90
|
+
- `client.templates`: `listProviders`, `get`
|
|
91
|
+
- top-level client methods: `usage()`, `sendTo()`, `buildRequest()`, `flow()`, `describe()`
|
|
92
|
+
|
|
62
93
|
## Endpoints
|
|
63
94
|
|
|
64
|
-
|
|
65
|
-
// Create
|
|
66
|
-
const endpoint = await client.endpoints.create({ name: "my-test" });
|
|
95
|
+
Create persistent or ephemeral endpoints. You can also attach a mock response at creation time.
|
|
67
96
|
|
|
68
|
-
|
|
69
|
-
const
|
|
97
|
+
```typescript
|
|
98
|
+
const endpoint = await client.endpoints.create({
|
|
99
|
+
name: "billing-webhooks",
|
|
100
|
+
expiresIn: "12h",
|
|
101
|
+
mockResponse: {
|
|
102
|
+
status: 202,
|
|
103
|
+
body: '{"queued":true}',
|
|
104
|
+
headers: { "x-webhooks-cc": "mock" },
|
|
105
|
+
},
|
|
106
|
+
});
|
|
70
107
|
|
|
71
|
-
|
|
72
|
-
|
|
108
|
+
const fetched = await client.endpoints.get(endpoint.slug);
|
|
109
|
+
console.log(fetched.isEphemeral, fetched.expiresAt);
|
|
73
110
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
mockResponse: { status: 201, body: '{"ok":true}', headers: {} },
|
|
111
|
+
await client.endpoints.update(endpoint.slug, {
|
|
112
|
+
name: "billing-webhooks-renamed",
|
|
113
|
+
mockResponse: null,
|
|
78
114
|
});
|
|
115
|
+
```
|
|
79
116
|
|
|
80
|
-
|
|
81
|
-
await client.endpoints.update("abc123", { mockResponse: null });
|
|
117
|
+
Send plain test requests through the hosted receiver:
|
|
82
118
|
|
|
83
|
-
|
|
84
|
-
|
|
119
|
+
```typescript
|
|
120
|
+
await client.endpoints.send(endpoint.slug, {
|
|
85
121
|
method: "POST",
|
|
86
122
|
headers: { "content-type": "application/json" },
|
|
87
|
-
body: { event: "
|
|
123
|
+
body: { event: "invoice.paid" },
|
|
88
124
|
});
|
|
89
|
-
|
|
90
|
-
// Delete
|
|
91
|
-
await client.endpoints.delete("abc123");
|
|
92
125
|
```
|
|
93
126
|
|
|
94
127
|
## Requests
|
|
95
128
|
|
|
129
|
+
List, paginate, wait, stream, replay, export, and clear captured requests.
|
|
130
|
+
|
|
96
131
|
```typescript
|
|
97
|
-
|
|
98
|
-
const requests = await client.requests.list("endpoint-slug", {
|
|
132
|
+
const recent = await client.requests.list(endpoint.slug, {
|
|
99
133
|
limit: 50,
|
|
100
|
-
since: Date.now() -
|
|
134
|
+
since: Date.now() - 60_000,
|
|
101
135
|
});
|
|
102
136
|
|
|
103
|
-
|
|
104
|
-
const
|
|
137
|
+
const page1 = await client.requests.listPaginated(endpoint.slug, { limit: 100 });
|
|
138
|
+
const page2 = page1.cursor
|
|
139
|
+
? await client.requests.listPaginated(endpoint.slug, { limit: 100, cursor: page1.cursor })
|
|
140
|
+
: { items: [], hasMore: false };
|
|
105
141
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
timeout: "30s", // human-readable or milliseconds
|
|
109
|
-
pollInterval: "500ms",
|
|
142
|
+
const firstMatch = await client.requests.waitFor(endpoint.slug, {
|
|
143
|
+
timeout: "20s",
|
|
110
144
|
match: matchHeader("stripe-signature"),
|
|
111
145
|
});
|
|
112
146
|
|
|
113
|
-
|
|
114
|
-
|
|
147
|
+
const allMatches = await client.requests.waitForAll(endpoint.slug, {
|
|
148
|
+
count: 3,
|
|
149
|
+
timeout: "30s",
|
|
150
|
+
match: matchMethod("POST"),
|
|
151
|
+
});
|
|
115
152
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
console.log(req.method, req.body);
|
|
153
|
+
for await (const request of client.requests.subscribe(endpoint.slug, { reconnect: true })) {
|
|
154
|
+
console.log(request.method, request.path);
|
|
119
155
|
}
|
|
120
156
|
```
|
|
121
157
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Composable functions for `waitFor`'s `match` option:
|
|
158
|
+
Replay, export, and clear requests:
|
|
125
159
|
|
|
126
160
|
```typescript
|
|
127
|
-
|
|
161
|
+
await client.requests.replay(firstMatch.id, "http://localhost:3001/webhooks");
|
|
162
|
+
|
|
163
|
+
const curlExport = await client.requests.export(endpoint.slug, {
|
|
164
|
+
format: "curl",
|
|
165
|
+
limit: 10,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const harExport = await client.requests.export(endpoint.slug, {
|
|
169
|
+
format: "har",
|
|
170
|
+
since: Date.now() - 3_600_000,
|
|
171
|
+
});
|
|
128
172
|
|
|
129
|
-
|
|
130
|
-
|
|
173
|
+
await client.requests.clear(endpoint.slug, { before: "24h" });
|
|
174
|
+
```
|
|
131
175
|
|
|
132
|
-
|
|
133
|
-
matchHeader("stripe-signature");
|
|
176
|
+
Search and count use the retained request store rather than the live endpoint request table:
|
|
134
177
|
|
|
135
|
-
|
|
136
|
-
|
|
178
|
+
```typescript
|
|
179
|
+
const retained = await client.requests.search({
|
|
180
|
+
slug: endpoint.slug,
|
|
181
|
+
q: "checkout.session.completed",
|
|
182
|
+
from: "7d",
|
|
183
|
+
limit: 20,
|
|
184
|
+
});
|
|
137
185
|
|
|
138
|
-
|
|
139
|
-
|
|
186
|
+
const total = await client.requests.count({
|
|
187
|
+
slug: endpoint.slug,
|
|
188
|
+
q: "checkout.session.completed",
|
|
189
|
+
from: "7d",
|
|
190
|
+
});
|
|
140
191
|
```
|
|
141
192
|
|
|
142
|
-
|
|
193
|
+
`search()` returns `SearchResult[]`. Their `id` field is synthetic and is not valid for `requests.get()` or `requests.replay()`.
|
|
194
|
+
|
|
195
|
+
## Templates, sendTo, and buildRequest
|
|
196
|
+
|
|
197
|
+
The SDK can generate signed webhook payloads for:
|
|
143
198
|
|
|
144
|
-
|
|
199
|
+
- `stripe`
|
|
200
|
+
- `github`
|
|
201
|
+
- `shopify`
|
|
202
|
+
- `twilio`
|
|
203
|
+
- `slack`
|
|
204
|
+
- `paddle`
|
|
205
|
+
- `linear`
|
|
206
|
+
- `standard-webhooks`
|
|
207
|
+
|
|
208
|
+
Inspect the static provider metadata:
|
|
145
209
|
|
|
146
210
|
```typescript
|
|
147
|
-
|
|
211
|
+
const providers = client.templates.listProviders();
|
|
212
|
+
const stripe = client.templates.get("stripe");
|
|
148
213
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
214
|
+
console.log(providers);
|
|
215
|
+
console.log(stripe.signatureHeader, stripe.templates);
|
|
152
216
|
```
|
|
153
217
|
|
|
154
|
-
|
|
218
|
+
If you prefer a static export, import `TEMPLATE_METADATA` from `@webhooks-cc/sdk`.
|
|
219
|
+
|
|
220
|
+
Send a signed provider template through a hosted endpoint:
|
|
155
221
|
|
|
156
|
-
|
|
222
|
+
```typescript
|
|
223
|
+
await client.endpoints.sendTemplate(endpoint.slug, {
|
|
224
|
+
provider: "slack",
|
|
225
|
+
template: "slash_command",
|
|
226
|
+
secret: process.env.SLACK_SIGNING_SECRET!,
|
|
227
|
+
});
|
|
228
|
+
```
|
|
157
229
|
|
|
158
|
-
|
|
230
|
+
Build or send a signed request directly to any URL:
|
|
159
231
|
|
|
160
232
|
```typescript
|
|
161
|
-
const
|
|
162
|
-
|
|
233
|
+
const preview = await client.buildRequest("http://localhost:3001/webhooks", {
|
|
234
|
+
provider: "stripe",
|
|
235
|
+
template: "checkout.session.completed",
|
|
236
|
+
secret: "whsec_test_123",
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
await client.sendTo("http://localhost:3001/webhooks", {
|
|
240
|
+
provider: "github",
|
|
241
|
+
template: "push",
|
|
242
|
+
secret: "github_secret",
|
|
243
|
+
});
|
|
163
244
|
```
|
|
164
245
|
|
|
165
|
-
##
|
|
246
|
+
## Signature verification
|
|
247
|
+
|
|
248
|
+
The SDK includes provider-specific verification helpers and a provider-agnostic `verifySignature()`.
|
|
249
|
+
|
|
250
|
+
Provider-specific helpers such as `verifyStripeSignature()` and `verifyDiscordSignature()` are also exported.
|
|
251
|
+
|
|
252
|
+
Supported verification providers:
|
|
166
253
|
|
|
167
|
-
|
|
254
|
+
- `stripe`
|
|
255
|
+
- `github`
|
|
256
|
+
- `shopify`
|
|
257
|
+
- `twilio`
|
|
258
|
+
- `slack`
|
|
259
|
+
- `paddle`
|
|
260
|
+
- `linear`
|
|
261
|
+
- `discord`
|
|
262
|
+
- `standard-webhooks`
|
|
168
263
|
|
|
169
264
|
```typescript
|
|
170
|
-
import {
|
|
265
|
+
import { isDiscordWebhook, verifySignature } from "@webhooks-cc/sdk";
|
|
171
266
|
|
|
172
|
-
|
|
173
|
-
await
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
267
|
+
if (isDiscordWebhook(request)) {
|
|
268
|
+
const result = await verifySignature(request, {
|
|
269
|
+
provider: "discord",
|
|
270
|
+
publicKey: process.env.DISCORD_PUBLIC_KEY!,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
console.log(result.valid);
|
|
179
274
|
}
|
|
180
275
|
```
|
|
181
276
|
|
|
182
|
-
|
|
277
|
+
For Twilio, pass the original signed URL:
|
|
183
278
|
|
|
184
|
-
|
|
279
|
+
```typescript
|
|
280
|
+
const result = await verifySignature(request, {
|
|
281
|
+
provider: "twilio",
|
|
282
|
+
secret: process.env.TWILIO_AUTH_TOKEN!,
|
|
283
|
+
url: "https://example.com/webhooks/twilio",
|
|
284
|
+
});
|
|
285
|
+
```
|
|
185
286
|
|
|
186
|
-
|
|
287
|
+
Discord support is verification-only. It is not part of the template generation API.
|
|
187
288
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
289
|
+
Request detection helpers are exported too: `isStripeWebhook()`, `isGitHubWebhook()`, `isShopifyWebhook()`, `isSlackWebhook()`, `isTwilioWebhook()`, `isPaddleWebhook()`, `isLinearWebhook()`, `isDiscordWebhook()`, and `isStandardWebhook()`.
|
|
290
|
+
|
|
291
|
+
## Matchers, parsing, and diffing
|
|
292
|
+
|
|
293
|
+
Use matchers with `waitFor()` or `waitForAll()`:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import {
|
|
297
|
+
matchAll,
|
|
298
|
+
matchBodySubset,
|
|
299
|
+
matchContentType,
|
|
300
|
+
matchHeader,
|
|
301
|
+
matchPath,
|
|
302
|
+
matchQueryParam,
|
|
303
|
+
} from "@webhooks-cc/sdk";
|
|
304
|
+
|
|
305
|
+
const request = await client.requests.waitFor(endpoint.slug, {
|
|
306
|
+
match: matchAll(
|
|
307
|
+
matchPath("/webhooks/stripe"),
|
|
308
|
+
matchHeader("stripe-signature"),
|
|
309
|
+
matchContentType("application/json"),
|
|
310
|
+
matchQueryParam("tenant", "acme"),
|
|
311
|
+
matchBodySubset({ type: "checkout.session.completed" })
|
|
312
|
+
),
|
|
313
|
+
});
|
|
193
314
|
```
|
|
194
315
|
|
|
316
|
+
`matchAny()`, `matchBodyPath()`, and `matchJsonField()` are available when you need looser matching.
|
|
317
|
+
|
|
318
|
+
Parse request bodies and diff captures:
|
|
319
|
+
|
|
195
320
|
```typescript
|
|
196
|
-
|
|
197
|
-
import { describe, it, expect, afterAll } from "vitest";
|
|
198
|
-
import { WebhooksCC, matchHeader } from "@webhooks-cc/sdk";
|
|
321
|
+
import { diffRequests, extractJsonField, parseBody, parseFormBody } from "@webhooks-cc/sdk";
|
|
199
322
|
|
|
200
|
-
const
|
|
323
|
+
const parsed = parseBody(request);
|
|
324
|
+
const form = parseFormBody(request);
|
|
325
|
+
const eventType = extractJsonField<string>(request, "type");
|
|
201
326
|
|
|
202
|
-
|
|
203
|
-
|
|
327
|
+
const diff = diffRequests(previousRequest, request, {
|
|
328
|
+
ignoreHeaders: ["date", "x-request-id"],
|
|
329
|
+
});
|
|
204
330
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
slug = endpoint.slug;
|
|
331
|
+
console.log(parsed, form, eventType, diff.matches);
|
|
332
|
+
```
|
|
208
333
|
|
|
209
|
-
|
|
210
|
-
await yourService.registerWebhook(endpoint.url!);
|
|
211
|
-
await yourService.createOrder();
|
|
334
|
+
## Testing helpers
|
|
212
335
|
|
|
213
|
-
|
|
214
|
-
timeout: "15s",
|
|
215
|
-
match: matchHeader("stripe-signature"),
|
|
216
|
-
});
|
|
336
|
+
`@webhooks-cc/sdk/testing` adds a small test-oriented layer:
|
|
217
337
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
338
|
+
- `withEndpoint()`
|
|
339
|
+
- `withEphemeralEndpoint()`
|
|
340
|
+
- `captureDuring()`
|
|
341
|
+
- `assertRequest()`
|
|
221
342
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
343
|
+
```typescript
|
|
344
|
+
import { matchHeader, WebhooksCC } from "@webhooks-cc/sdk";
|
|
345
|
+
import { assertRequest, captureDuring } from "@webhooks-cc/sdk/testing";
|
|
346
|
+
|
|
347
|
+
const client = new WebhooksCC({ apiKey: process.env.WHK_API_KEY! });
|
|
348
|
+
|
|
349
|
+
const [request] = await captureDuring(
|
|
350
|
+
client,
|
|
351
|
+
async (endpoint) => {
|
|
352
|
+
await yourApp.registerWebhook(endpoint.url!);
|
|
353
|
+
await yourApp.triggerCheckout();
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
expiresIn: "1h",
|
|
357
|
+
timeout: "20s",
|
|
358
|
+
match: matchHeader("stripe-signature"),
|
|
359
|
+
}
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
assertRequest(
|
|
363
|
+
request,
|
|
364
|
+
{
|
|
365
|
+
method: "POST",
|
|
366
|
+
bodyJson: { type: "checkout.session.completed" },
|
|
367
|
+
},
|
|
368
|
+
{ throwOnFailure: true }
|
|
369
|
+
);
|
|
226
370
|
```
|
|
227
371
|
|
|
228
|
-
##
|
|
372
|
+
## Flow builder
|
|
229
373
|
|
|
230
|
-
|
|
374
|
+
`client.flow()` composes the common test sequence into one chain: create endpoint, optionally set a mock, send a request, wait for capture, verify the signature, replay the request, and clean up.
|
|
231
375
|
|
|
232
376
|
```typescript
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
377
|
+
const result = await client
|
|
378
|
+
.flow()
|
|
379
|
+
.createEndpoint({ expiresIn: "1h" })
|
|
380
|
+
.sendTemplate({
|
|
381
|
+
provider: "github",
|
|
382
|
+
template: "push",
|
|
383
|
+
secret: "github_secret",
|
|
384
|
+
})
|
|
385
|
+
.waitForCapture({ timeout: "15s" })
|
|
386
|
+
.verifySignature({
|
|
387
|
+
provider: "github",
|
|
388
|
+
secret: "github_secret",
|
|
389
|
+
})
|
|
390
|
+
.cleanup()
|
|
391
|
+
.run();
|
|
392
|
+
|
|
393
|
+
console.log(result.request?.id, result.verification?.valid, result.cleanedUp);
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Usage and self-description
|
|
397
|
+
|
|
398
|
+
Check quota state from code:
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
const usage = await client.usage();
|
|
402
|
+
console.log(usage.used, usage.limit, usage.remaining, usage.plan);
|
|
246
403
|
```
|
|
247
404
|
|
|
405
|
+
Ask the client what it supports without making an API call:
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
const description = client.describe();
|
|
409
|
+
console.log(description.requests.waitForAll);
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Errors
|
|
413
|
+
|
|
414
|
+
API failures throw typed errors:
|
|
415
|
+
|
|
416
|
+
- `WebhooksCCError`
|
|
417
|
+
- `UnauthorizedError`
|
|
418
|
+
- `NotFoundError`
|
|
419
|
+
- `TimeoutError`
|
|
420
|
+
- `RateLimitError`
|
|
421
|
+
|
|
422
|
+
`ApiError` is still exported as a legacy alias of `WebhooksCCError`.
|
|
423
|
+
|
|
248
424
|
## License
|
|
249
425
|
|
|
250
426
|
MIT
|