@linqapp/sdk 0.25.0 → 0.26.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/CHANGELOG.md +2 -2
- package/api-promise.d.mts +1 -0
- package/api-promise.d.mts.map +1 -1
- package/api-promise.d.ts +1 -0
- package/api-promise.d.ts.map +1 -1
- package/api-promise.mjs +1 -0
- package/api-promise.mjs.map +1 -1
- package/client.d.mts +162 -84
- package/client.d.mts.map +1 -1
- package/client.d.ts +162 -84
- package/client.d.ts.map +1 -1
- package/client.js +162 -84
- package/client.js.map +1 -1
- package/client.mjs +162 -84
- package/client.mjs.map +1 -1
- package/core/api-promise.d.mts.map +1 -1
- package/core/api-promise.d.ts.map +1 -1
- package/core/api-promise.mjs.map +1 -1
- package/core/error.mjs.map +1 -1
- package/core/pagination.d.mts.map +1 -1
- package/core/pagination.d.ts.map +1 -1
- package/core/pagination.mjs.map +1 -1
- package/core/resource.d.mts.map +1 -1
- package/core/resource.d.ts.map +1 -1
- package/core/uploads.d.mts.map +1 -1
- package/core/uploads.d.ts.map +1 -1
- package/core/uploads.mjs.map +1 -1
- package/error.d.mts +1 -0
- package/error.d.mts.map +1 -1
- package/error.d.ts +1 -0
- package/error.d.ts.map +1 -1
- package/error.mjs +1 -0
- package/error.mjs.map +1 -1
- package/index.d.mts.map +1 -1
- package/index.d.ts.map +1 -1
- package/index.mjs.map +1 -1
- package/internal/detect-platform.mjs.map +1 -1
- package/internal/headers.mjs.map +1 -1
- package/internal/parse.d.mts.map +1 -1
- package/internal/parse.d.ts.map +1 -1
- package/internal/parse.mjs.map +1 -1
- package/internal/request-options.d.mts.map +1 -1
- package/internal/request-options.d.ts.map +1 -1
- package/internal/shims.d.mts +6 -0
- package/internal/shims.d.mts.map +1 -1
- package/internal/shims.d.ts +6 -0
- package/internal/shims.d.ts.map +1 -1
- package/internal/to-file.d.mts.map +1 -1
- package/internal/to-file.d.ts.map +1 -1
- package/internal/to-file.mjs.map +1 -1
- package/internal/types.d.mts +4 -4
- package/internal/types.d.mts.map +1 -1
- package/internal/types.d.ts +4 -4
- package/internal/types.d.ts.map +1 -1
- package/internal/uploads.d.mts.map +1 -1
- package/internal/uploads.d.ts.map +1 -1
- package/internal/uploads.mjs.map +1 -1
- package/internal/utils/base64.mjs.map +1 -1
- package/internal/utils/log.d.mts.map +1 -1
- package/internal/utils/log.d.ts.map +1 -1
- package/internal/utils/log.mjs.map +1 -1
- package/internal/utils/path.mjs.map +1 -1
- package/internal/utils/query.mjs.map +1 -1
- package/internal/utils/values.mjs.map +1 -1
- package/internal/utils.d.mts.map +1 -1
- package/internal/utils.d.ts.map +1 -1
- package/internal/utils.mjs.map +1 -1
- package/package.json +1 -1
- package/pagination.d.mts +1 -0
- package/pagination.d.mts.map +1 -1
- package/pagination.d.ts +1 -0
- package/pagination.d.ts.map +1 -1
- package/pagination.mjs +1 -0
- package/pagination.mjs.map +1 -1
- package/resource.d.mts +1 -0
- package/resource.d.mts.map +1 -1
- package/resource.d.ts +1 -0
- package/resource.d.ts.map +1 -1
- package/resource.mjs +1 -0
- package/resource.mjs.map +1 -1
- package/resources/attachments.d.mts.map +1 -1
- package/resources/attachments.d.ts.map +1 -1
- package/resources/attachments.mjs.map +1 -1
- package/resources/capability.d.mts.map +1 -1
- package/resources/capability.d.ts.map +1 -1
- package/resources/capability.mjs.map +1 -1
- package/resources/chats/chats.d.mts +4 -0
- package/resources/chats/chats.d.mts.map +1 -1
- package/resources/chats/chats.d.ts +4 -0
- package/resources/chats/chats.d.ts.map +1 -1
- package/resources/chats/chats.js +4 -0
- package/resources/chats/chats.js.map +1 -1
- package/resources/chats/chats.mjs +4 -0
- package/resources/chats/chats.mjs.map +1 -1
- package/resources/chats/index.d.mts +1 -0
- package/resources/chats/index.d.mts.map +1 -1
- package/resources/chats/index.d.ts +1 -0
- package/resources/chats/index.d.ts.map +1 -1
- package/resources/chats/index.js +3 -1
- package/resources/chats/index.js.map +1 -1
- package/resources/chats/index.mjs +1 -0
- package/resources/chats/index.mjs.map +1 -1
- package/resources/chats/location.d.mts +107 -0
- package/resources/chats/location.d.mts.map +1 -0
- package/resources/chats/location.d.ts +107 -0
- package/resources/chats/location.d.ts.map +1 -0
- package/resources/chats/location.js +68 -0
- package/resources/chats/location.js.map +1 -0
- package/resources/chats/location.mjs +64 -0
- package/resources/chats/location.mjs.map +1 -0
- package/resources/chats/messages.d.mts.map +1 -1
- package/resources/chats/messages.d.ts.map +1 -1
- package/resources/chats/messages.mjs.map +1 -1
- package/resources/chats/participants.d.mts.map +1 -1
- package/resources/chats/participants.d.ts.map +1 -1
- package/resources/chats/participants.mjs.map +1 -1
- package/resources/chats/typing.d.mts.map +1 -1
- package/resources/chats/typing.d.ts.map +1 -1
- package/resources/chats/typing.mjs.map +1 -1
- package/resources/chats.d.mts.map +1 -1
- package/resources/chats.d.ts.map +1 -1
- package/resources/chats.mjs.map +1 -1
- package/resources/contact-card.d.mts.map +1 -1
- package/resources/contact-card.d.ts.map +1 -1
- package/resources/contact-card.mjs.map +1 -1
- package/resources/index.d.mts.map +1 -1
- package/resources/index.d.ts.map +1 -1
- package/resources/index.mjs.map +1 -1
- package/resources/messages.d.mts.map +1 -1
- package/resources/messages.d.ts.map +1 -1
- package/resources/messages.mjs.map +1 -1
- package/resources/phone-numbers.d.mts.map +1 -1
- package/resources/phone-numbers.d.ts.map +1 -1
- package/resources/phone-numbers.mjs.map +1 -1
- package/resources/phonenumbers.d.mts.map +1 -1
- package/resources/phonenumbers.d.ts.map +1 -1
- package/resources/phonenumbers.mjs.map +1 -1
- package/resources/webhook-events.d.mts +83 -44
- package/resources/webhook-events.d.mts.map +1 -1
- package/resources/webhook-events.d.ts +83 -44
- package/resources/webhook-events.d.ts.map +1 -1
- package/resources/webhook-events.js +81 -42
- package/resources/webhook-events.js.map +1 -1
- package/resources/webhook-events.mjs +81 -42
- package/resources/webhook-events.mjs.map +1 -1
- package/resources/webhook-subscriptions.d.mts +89 -44
- package/resources/webhook-subscriptions.d.mts.map +1 -1
- package/resources/webhook-subscriptions.d.ts +89 -44
- package/resources/webhook-subscriptions.d.ts.map +1 -1
- package/resources/webhook-subscriptions.js +90 -45
- package/resources/webhook-subscriptions.js.map +1 -1
- package/resources/webhook-subscriptions.mjs +90 -45
- package/resources/webhook-subscriptions.mjs.map +1 -1
- package/resources/webhooks.d.mts +4 -4
- package/resources/webhooks.d.mts.map +1 -1
- package/resources/webhooks.d.ts +4 -4
- package/resources/webhooks.d.ts.map +1 -1
- package/resources/webhooks.mjs.map +1 -1
- package/resources.d.mts.map +1 -1
- package/resources.d.ts.map +1 -1
- package/resources.mjs.map +1 -1
- package/src/client.ts +162 -84
- package/src/resources/chats/chats.ts +10 -0
- package/src/resources/chats/index.ts +1 -0
- package/src/resources/chats/location.ts +138 -0
- package/src/resources/webhook-events.ts +85 -44
- package/src/resources/webhook-subscriptions.ts +89 -44
- package/src/resources/webhooks.ts +6 -4
- package/src/version.ts +1 -1
- package/uploads.d.mts +1 -0
- package/uploads.d.mts.map +1 -1
- package/uploads.d.ts +1 -0
- package/uploads.d.ts.map +1 -1
- package/uploads.mjs +1 -0
- package/uploads.mjs.map +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
|
@@ -17,55 +17,82 @@ import { RequestOptions } from '../internal/request-options';
|
|
|
17
17
|
*
|
|
18
18
|
* ## Webhook Headers
|
|
19
19
|
*
|
|
20
|
-
*
|
|
20
|
+
* All webhook requests include two sets of headers. **If you have an existing integration
|
|
21
|
+
* using the `X-Webhook-*` headers, nothing changes** — those headers are still sent on
|
|
22
|
+
* every delivery and work exactly as before. The new `webhook-*` headers follow the
|
|
23
|
+
* [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks) specification.
|
|
24
|
+
* You can safely ignore them if your current verification code works and you don't want to use this convention.
|
|
25
|
+
*
|
|
26
|
+
* ### Standard Webhooks Headers (Recommended)
|
|
27
|
+
*
|
|
28
|
+
* Used by [our SDK](https://github.com/linq-team/linq-node) and any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks).
|
|
21
29
|
*
|
|
22
30
|
* | Header | Description |
|
|
23
31
|
* |--------|-------------|
|
|
24
|
-
* | `
|
|
25
|
-
* | `
|
|
26
|
-
* | `
|
|
27
|
-
* | `X-Webhook-Signature` | HMAC-SHA256 signature for verification |
|
|
32
|
+
* | `webhook-id` | Unique event identifier (use as idempotency key) |
|
|
33
|
+
* | `webhook-timestamp` | Unix timestamp (seconds) when the webhook was sent |
|
|
34
|
+
* | `webhook-signature` | Standard Webhooks signature (`v1,{base64}` format) |
|
|
28
35
|
*
|
|
29
|
-
*
|
|
36
|
+
* ### Legacy Headers (Deprecated)
|
|
37
|
+
*
|
|
38
|
+
* Still sent on every delivery for backwards compatibility. Existing verification code
|
|
39
|
+
* using these headers continues to work — no changes required.
|
|
30
40
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
41
|
+
* | Header | Description |
|
|
42
|
+
* |--------|-------------|
|
|
43
|
+
* | `X-Webhook-Event` | *(deprecated)* Event type (e.g., `message.sent`) |
|
|
44
|
+
* | `X-Webhook-Subscription-ID` | *(deprecated)* Webhook subscription ID |
|
|
45
|
+
* | `X-Webhook-Timestamp` | *(deprecated)* Unix timestamp (seconds) |
|
|
46
|
+
* | `X-Webhook-Signature` | *(deprecated)* HMAC-SHA256 signature (hex-encoded) |
|
|
33
47
|
*
|
|
34
|
-
*
|
|
48
|
+
* ## Signing Secrets
|
|
35
49
|
*
|
|
36
|
-
*
|
|
50
|
+
* Signing secrets use the Standard Webhooks format: a `whsec_` prefix followed
|
|
51
|
+
* by base64-encoded random bytes (e.g., `whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw7Jxx2Oll+OE=`).
|
|
37
52
|
*
|
|
38
|
-
*
|
|
39
|
-
* {timestamp}.{payload}
|
|
40
|
-
* ```
|
|
53
|
+
* Strip the `whsec_` prefix and base64-decode the remainder to get the raw key bytes.
|
|
41
54
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
55
|
+
* ## Verifying Webhook Signatures
|
|
56
|
+
*
|
|
57
|
+
* Webhooks are signed following the [Standard Webhooks specification](https://github.com/standard-webhooks/standard-webhooks).
|
|
58
|
+
* You can use any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks) to verify
|
|
59
|
+
* signatures, or implement verification manually:
|
|
60
|
+
*
|
|
61
|
+
* **Signed content:** `{webhook-id}.{webhook-timestamp}.{body}`
|
|
45
62
|
*
|
|
46
63
|
* **Verification Steps:**
|
|
47
64
|
*
|
|
48
|
-
* 1. Extract the `
|
|
49
|
-
* 2.
|
|
50
|
-
* 3.
|
|
51
|
-
* 4.
|
|
52
|
-
* 5.
|
|
53
|
-
* 6.
|
|
65
|
+
* 1. Extract the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers
|
|
66
|
+
* 2. Reject if the timestamp is more than 5 minutes old (replay protection)
|
|
67
|
+
* 3. Get the raw request body bytes (do not parse and re-serialize)
|
|
68
|
+
* 4. Construct signed content: `"{webhook-id}.{webhook-timestamp}.{body}"`
|
|
69
|
+
* 5. Strip the `whsec_` prefix from your secret and base64-decode to get key bytes
|
|
70
|
+
* 6. Compute HMAC-SHA256 using the key bytes over the signed content
|
|
71
|
+
* 7. Base64-encode the result and compare with the value after `v1,` in `webhook-signature`
|
|
72
|
+
* 8. Use constant-time comparison to prevent timing attacks
|
|
54
73
|
*
|
|
55
74
|
* **Example (Python):**
|
|
56
75
|
*
|
|
57
76
|
* ```python
|
|
58
|
-
* import hmac
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
77
|
+
* import base64, hmac, hashlib
|
|
78
|
+
*
|
|
79
|
+
* def verify_webhook(secret, body, headers):
|
|
80
|
+
* msg_id = headers['webhook-id']
|
|
81
|
+
* timestamp = headers['webhook-timestamp']
|
|
82
|
+
* signature = headers['webhook-signature']
|
|
83
|
+
*
|
|
84
|
+
* secret_str = secret.removeprefix('whsec_')
|
|
85
|
+
* key = base64.b64decode(secret_str)
|
|
86
|
+
*
|
|
87
|
+
* signed_content = f"{msg_id}.{timestamp}.{body}"
|
|
88
|
+
* expected = base64.b64encode(
|
|
89
|
+
* hmac.new(key, signed_content.encode(), hashlib.sha256).digest()
|
|
90
|
+
* ).decode()
|
|
91
|
+
*
|
|
92
|
+
* for sig in signature.split(' '):
|
|
93
|
+
* if sig.startswith('v1,') and hmac.compare_digest(expected, sig[3:]):
|
|
94
|
+
* return True
|
|
95
|
+
* return False
|
|
69
96
|
* ```
|
|
70
97
|
*
|
|
71
98
|
* **Example (Node.js):**
|
|
@@ -73,16 +100,28 @@ import { RequestOptions } from '../internal/request-options';
|
|
|
73
100
|
* ```javascript
|
|
74
101
|
* const crypto = require('crypto');
|
|
75
102
|
*
|
|
76
|
-
* function verifyWebhook(
|
|
77
|
-
* const
|
|
103
|
+
* function verifyWebhook(secret, rawBody, headers) {
|
|
104
|
+
* const msgId = headers['webhook-id'];
|
|
105
|
+
* const timestamp = headers['webhook-timestamp'];
|
|
106
|
+
* const signature = headers['webhook-signature'];
|
|
107
|
+
*
|
|
108
|
+
* const secretStr = secret.startsWith('whsec_') ? secret.slice(6) : secret;
|
|
109
|
+
* const keyBytes = Buffer.from(secretStr, 'base64');
|
|
110
|
+
* const signedContent = `${msgId}.${timestamp}.${rawBody}`;
|
|
78
111
|
* const expected = crypto
|
|
79
|
-
* .createHmac('sha256',
|
|
80
|
-
* .update(
|
|
81
|
-
* .digest('
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
112
|
+
* .createHmac('sha256', keyBytes)
|
|
113
|
+
* .update(signedContent)
|
|
114
|
+
* .digest('base64');
|
|
115
|
+
*
|
|
116
|
+
* return signature.split(' ').some(sig => {
|
|
117
|
+
* if (!sig.startsWith('v1,')) return false;
|
|
118
|
+
* try {
|
|
119
|
+
* return crypto.timingSafeEqual(
|
|
120
|
+
* Buffer.from(expected, 'base64'),
|
|
121
|
+
* Buffer.from(sig.slice(3), 'base64')
|
|
122
|
+
* );
|
|
123
|
+
* } catch { return false; }
|
|
124
|
+
* });
|
|
86
125
|
* }
|
|
87
126
|
* ```
|
|
88
127
|
*
|
|
@@ -136,13 +175,15 @@ export type WebhookEventType =
|
|
|
136
175
|
| 'call.ended'
|
|
137
176
|
| 'call.failed'
|
|
138
177
|
| 'call.declined'
|
|
139
|
-
| 'call.no_answer'
|
|
178
|
+
| 'call.no_answer'
|
|
179
|
+
| 'location.sharing.started'
|
|
180
|
+
| 'location.sharing.stopped';
|
|
140
181
|
|
|
141
182
|
export interface WebhookEventListResponse {
|
|
142
183
|
/**
|
|
143
184
|
* URL to the webhook events documentation
|
|
144
185
|
*/
|
|
145
|
-
doc_url: 'https://
|
|
186
|
+
doc_url: 'https://docs.linqapp.com/guides/webhooks/events';
|
|
146
187
|
|
|
147
188
|
/**
|
|
148
189
|
* List of all available webhook event types
|
|
@@ -20,55 +20,82 @@ import { path } from '../internal/utils/path';
|
|
|
20
20
|
*
|
|
21
21
|
* ## Webhook Headers
|
|
22
22
|
*
|
|
23
|
-
*
|
|
23
|
+
* All webhook requests include two sets of headers. **If you have an existing integration
|
|
24
|
+
* using the `X-Webhook-*` headers, nothing changes** — those headers are still sent on
|
|
25
|
+
* every delivery and work exactly as before. The new `webhook-*` headers follow the
|
|
26
|
+
* [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks) specification.
|
|
27
|
+
* You can safely ignore them if your current verification code works and you don't want to use this convention.
|
|
28
|
+
*
|
|
29
|
+
* ### Standard Webhooks Headers (Recommended)
|
|
30
|
+
*
|
|
31
|
+
* Used by [our SDK](https://github.com/linq-team/linq-node) and any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks).
|
|
24
32
|
*
|
|
25
33
|
* | Header | Description |
|
|
26
34
|
* |--------|-------------|
|
|
27
|
-
* | `
|
|
28
|
-
* | `
|
|
29
|
-
* | `
|
|
30
|
-
* | `X-Webhook-Signature` | HMAC-SHA256 signature for verification |
|
|
35
|
+
* | `webhook-id` | Unique event identifier (use as idempotency key) |
|
|
36
|
+
* | `webhook-timestamp` | Unix timestamp (seconds) when the webhook was sent |
|
|
37
|
+
* | `webhook-signature` | Standard Webhooks signature (`v1,{base64}` format) |
|
|
31
38
|
*
|
|
32
|
-
*
|
|
39
|
+
* ### Legacy Headers (Deprecated)
|
|
40
|
+
*
|
|
41
|
+
* Still sent on every delivery for backwards compatibility. Existing verification code
|
|
42
|
+
* using these headers continues to work — no changes required.
|
|
33
43
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
44
|
+
* | Header | Description |
|
|
45
|
+
* |--------|-------------|
|
|
46
|
+
* | `X-Webhook-Event` | *(deprecated)* Event type (e.g., `message.sent`) |
|
|
47
|
+
* | `X-Webhook-Subscription-ID` | *(deprecated)* Webhook subscription ID |
|
|
48
|
+
* | `X-Webhook-Timestamp` | *(deprecated)* Unix timestamp (seconds) |
|
|
49
|
+
* | `X-Webhook-Signature` | *(deprecated)* HMAC-SHA256 signature (hex-encoded) |
|
|
36
50
|
*
|
|
37
|
-
*
|
|
51
|
+
* ## Signing Secrets
|
|
38
52
|
*
|
|
39
|
-
*
|
|
53
|
+
* Signing secrets use the Standard Webhooks format: a `whsec_` prefix followed
|
|
54
|
+
* by base64-encoded random bytes (e.g., `whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw7Jxx2Oll+OE=`).
|
|
40
55
|
*
|
|
41
|
-
*
|
|
42
|
-
* {timestamp}.{payload}
|
|
43
|
-
* ```
|
|
56
|
+
* Strip the `whsec_` prefix and base64-decode the remainder to get the raw key bytes.
|
|
44
57
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
58
|
+
* ## Verifying Webhook Signatures
|
|
59
|
+
*
|
|
60
|
+
* Webhooks are signed following the [Standard Webhooks specification](https://github.com/standard-webhooks/standard-webhooks).
|
|
61
|
+
* You can use any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks) to verify
|
|
62
|
+
* signatures, or implement verification manually:
|
|
63
|
+
*
|
|
64
|
+
* **Signed content:** `{webhook-id}.{webhook-timestamp}.{body}`
|
|
48
65
|
*
|
|
49
66
|
* **Verification Steps:**
|
|
50
67
|
*
|
|
51
|
-
* 1. Extract the `
|
|
52
|
-
* 2.
|
|
53
|
-
* 3.
|
|
54
|
-
* 4.
|
|
55
|
-
* 5.
|
|
56
|
-
* 6.
|
|
68
|
+
* 1. Extract the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers
|
|
69
|
+
* 2. Reject if the timestamp is more than 5 minutes old (replay protection)
|
|
70
|
+
* 3. Get the raw request body bytes (do not parse and re-serialize)
|
|
71
|
+
* 4. Construct signed content: `"{webhook-id}.{webhook-timestamp}.{body}"`
|
|
72
|
+
* 5. Strip the `whsec_` prefix from your secret and base64-decode to get key bytes
|
|
73
|
+
* 6. Compute HMAC-SHA256 using the key bytes over the signed content
|
|
74
|
+
* 7. Base64-encode the result and compare with the value after `v1,` in `webhook-signature`
|
|
75
|
+
* 8. Use constant-time comparison to prevent timing attacks
|
|
57
76
|
*
|
|
58
77
|
* **Example (Python):**
|
|
59
78
|
*
|
|
60
79
|
* ```python
|
|
61
|
-
* import hmac
|
|
62
|
-
*
|
|
80
|
+
* import base64, hmac, hashlib
|
|
81
|
+
*
|
|
82
|
+
* def verify_webhook(secret, body, headers):
|
|
83
|
+
* msg_id = headers['webhook-id']
|
|
84
|
+
* timestamp = headers['webhook-timestamp']
|
|
85
|
+
* signature = headers['webhook-signature']
|
|
63
86
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* hashlib.sha256
|
|
70
|
-
* ).
|
|
71
|
-
*
|
|
87
|
+
* secret_str = secret.removeprefix('whsec_')
|
|
88
|
+
* key = base64.b64decode(secret_str)
|
|
89
|
+
*
|
|
90
|
+
* signed_content = f"{msg_id}.{timestamp}.{body}"
|
|
91
|
+
* expected = base64.b64encode(
|
|
92
|
+
* hmac.new(key, signed_content.encode(), hashlib.sha256).digest()
|
|
93
|
+
* ).decode()
|
|
94
|
+
*
|
|
95
|
+
* for sig in signature.split(' '):
|
|
96
|
+
* if sig.startswith('v1,') and hmac.compare_digest(expected, sig[3:]):
|
|
97
|
+
* return True
|
|
98
|
+
* return False
|
|
72
99
|
* ```
|
|
73
100
|
*
|
|
74
101
|
* **Example (Node.js):**
|
|
@@ -76,16 +103,28 @@ import { path } from '../internal/utils/path';
|
|
|
76
103
|
* ```javascript
|
|
77
104
|
* const crypto = require('crypto');
|
|
78
105
|
*
|
|
79
|
-
* function verifyWebhook(
|
|
80
|
-
* const
|
|
106
|
+
* function verifyWebhook(secret, rawBody, headers) {
|
|
107
|
+
* const msgId = headers['webhook-id'];
|
|
108
|
+
* const timestamp = headers['webhook-timestamp'];
|
|
109
|
+
* const signature = headers['webhook-signature'];
|
|
110
|
+
*
|
|
111
|
+
* const secretStr = secret.startsWith('whsec_') ? secret.slice(6) : secret;
|
|
112
|
+
* const keyBytes = Buffer.from(secretStr, 'base64');
|
|
113
|
+
* const signedContent = `${msgId}.${timestamp}.${rawBody}`;
|
|
81
114
|
* const expected = crypto
|
|
82
|
-
* .createHmac('sha256',
|
|
83
|
-
* .update(
|
|
84
|
-
* .digest('
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
115
|
+
* .createHmac('sha256', keyBytes)
|
|
116
|
+
* .update(signedContent)
|
|
117
|
+
* .digest('base64');
|
|
118
|
+
*
|
|
119
|
+
* return signature.split(' ').some(sig => {
|
|
120
|
+
* if (!sig.startsWith('v1,')) return false;
|
|
121
|
+
* try {
|
|
122
|
+
* return crypto.timingSafeEqual(
|
|
123
|
+
* Buffer.from(expected, 'base64'),
|
|
124
|
+
* Buffer.from(sig.slice(3), 'base64')
|
|
125
|
+
* );
|
|
126
|
+
* } catch { return false; }
|
|
127
|
+
* });
|
|
89
128
|
* }
|
|
90
129
|
* ```
|
|
91
130
|
*
|
|
@@ -115,9 +154,15 @@ export class WebhookSubscriptions extends APIResource {
|
|
|
115
154
|
* **Webhook Delivery:**
|
|
116
155
|
*
|
|
117
156
|
* - Events are sent via HTTP POST to the target URL
|
|
118
|
-
* - Each request includes
|
|
119
|
-
* -
|
|
120
|
-
*
|
|
157
|
+
* - Each request includes
|
|
158
|
+
* [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks)
|
|
159
|
+
* headers (`webhook-id`, `webhook-timestamp`, `webhook-signature`) for signature
|
|
160
|
+
* verification
|
|
161
|
+
* - Legacy `X-Webhook-*` headers are also sent for backwards compatibility
|
|
162
|
+
* (deprecated)
|
|
163
|
+
* - See
|
|
164
|
+
* [Verifying Webhook Signatures](https://docs.linqapp.com/guides/webhooks#verifying-webhook-signatures)
|
|
165
|
+
* for verification details
|
|
121
166
|
* - Failed deliveries (5xx, 429, network errors) are retried up to 10 times over
|
|
122
167
|
* ~25 minutes with exponential backoff
|
|
123
168
|
* - Client errors (4xx except 429) are not retried
|
|
@@ -794,7 +794,7 @@ export namespace MessageFailedWebhookEvent {
|
|
|
794
794
|
*/
|
|
795
795
|
export interface Data {
|
|
796
796
|
/**
|
|
797
|
-
* Error codes in webhook failure events (3007, 4001).
|
|
797
|
+
* Error codes in webhook failure events (3007, 4001, 4005).
|
|
798
798
|
*/
|
|
799
799
|
code: number;
|
|
800
800
|
|
|
@@ -1638,7 +1638,7 @@ export namespace ChatGroupNameUpdateFailedWebhookEvent {
|
|
|
1638
1638
|
chat_id: string;
|
|
1639
1639
|
|
|
1640
1640
|
/**
|
|
1641
|
-
* Error codes in webhook failure events (3007, 4001).
|
|
1641
|
+
* Error codes in webhook failure events (3007, 4001, 4005).
|
|
1642
1642
|
*/
|
|
1643
1643
|
error_code: number;
|
|
1644
1644
|
|
|
@@ -1715,7 +1715,7 @@ export namespace ChatGroupIconUpdateFailedWebhookEvent {
|
|
|
1715
1715
|
chat_id: string;
|
|
1716
1716
|
|
|
1717
1717
|
/**
|
|
1718
|
-
* Error codes in webhook failure events (3007, 4001).
|
|
1718
|
+
* Error codes in webhook failure events (3007, 4001, 4005).
|
|
1719
1719
|
*/
|
|
1720
1720
|
error_code: number;
|
|
1721
1721
|
|
|
@@ -1904,7 +1904,9 @@ export interface PhoneNumberStatusUpdatedWebhookEvent {
|
|
|
1904
1904
|
| 'call.ended'
|
|
1905
1905
|
| 'call.failed'
|
|
1906
1906
|
| 'call.declined'
|
|
1907
|
-
| 'call.no_answer'
|
|
1907
|
+
| 'call.no_answer'
|
|
1908
|
+
| 'location.sharing.started'
|
|
1909
|
+
| 'location.sharing.stopped';
|
|
1908
1910
|
|
|
1909
1911
|
/**
|
|
1910
1912
|
* Partner identifier. Present on all webhooks for cross-referencing.
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.
|
|
1
|
+
export const VERSION = '0.26.0'; // x-release-please-version
|
package/uploads.d.mts
CHANGED
package/uploads.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploads.d.mts","sourceRoot":"","sources":["src/uploads.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"uploads.d.mts","sourceRoot":"","sources":["src/uploads.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,mCAA+B"}
|
package/uploads.d.ts
CHANGED
package/uploads.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["src/uploads.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["src/uploads.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,kCAA+B"}
|
package/uploads.mjs
CHANGED
package/uploads.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploads.mjs","sourceRoot":"","sources":["src/uploads.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"uploads.mjs","sourceRoot":"","sources":["src/uploads.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,mCAA+B"}
|
package/version.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.26.0";
|
|
2
2
|
//# sourceMappingURL=version.d.mts.map
|
package/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.26.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/version.js
CHANGED
package/version.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = '0.
|
|
1
|
+
export const VERSION = '0.26.0'; // x-release-please-version
|
|
2
2
|
//# sourceMappingURL=version.mjs.map
|