@linqapp/sdk 0.24.2 → 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.
Files changed (98) hide show
  1. package/CHANGELOG.md +2 -506
  2. package/client.d.mts +165 -87
  3. package/client.d.mts.map +1 -1
  4. package/client.d.ts +165 -87
  5. package/client.d.ts.map +1 -1
  6. package/client.js +162 -84
  7. package/client.js.map +1 -1
  8. package/client.mjs +162 -84
  9. package/client.mjs.map +1 -1
  10. package/package.json +1 -1
  11. package/resources/chats/chats.d.mts +67 -63
  12. package/resources/chats/chats.d.mts.map +1 -1
  13. package/resources/chats/chats.d.ts +67 -63
  14. package/resources/chats/chats.d.ts.map +1 -1
  15. package/resources/chats/chats.js +63 -59
  16. package/resources/chats/chats.js.map +1 -1
  17. package/resources/chats/chats.mjs +63 -59
  18. package/resources/chats/chats.mjs.map +1 -1
  19. package/resources/chats/index.d.mts +3 -2
  20. package/resources/chats/index.d.mts.map +1 -1
  21. package/resources/chats/index.d.ts +3 -2
  22. package/resources/chats/index.d.ts.map +1 -1
  23. package/resources/chats/index.js +3 -1
  24. package/resources/chats/index.js.map +1 -1
  25. package/resources/chats/index.mjs +1 -0
  26. package/resources/chats/index.mjs.map +1 -1
  27. package/resources/chats/location.d.mts +107 -0
  28. package/resources/chats/location.d.mts.map +1 -0
  29. package/resources/chats/location.d.ts +107 -0
  30. package/resources/chats/location.d.ts.map +1 -0
  31. package/resources/chats/location.js +68 -0
  32. package/resources/chats/location.js.map +1 -0
  33. package/resources/chats/location.mjs +64 -0
  34. package/resources/chats/location.mjs.map +1 -0
  35. package/resources/chats/messages.d.mts +17 -17
  36. package/resources/chats/messages.d.mts.map +1 -1
  37. package/resources/chats/messages.d.ts +17 -17
  38. package/resources/chats/messages.d.ts.map +1 -1
  39. package/resources/chats/messages.js +16 -16
  40. package/resources/chats/messages.js.map +1 -1
  41. package/resources/chats/messages.mjs +16 -16
  42. package/resources/chats/messages.mjs.map +1 -1
  43. package/resources/contact-card.d.mts +18 -18
  44. package/resources/contact-card.d.mts.map +1 -1
  45. package/resources/contact-card.d.ts +18 -18
  46. package/resources/contact-card.d.ts.map +1 -1
  47. package/resources/contact-card.js +12 -12
  48. package/resources/contact-card.js.map +1 -1
  49. package/resources/contact-card.mjs +12 -12
  50. package/resources/contact-card.mjs.map +1 -1
  51. package/resources/index.d.mts +3 -3
  52. package/resources/index.d.mts.map +1 -1
  53. package/resources/index.d.ts +3 -3
  54. package/resources/index.d.ts.map +1 -1
  55. package/resources/messages.d.mts +34 -34
  56. package/resources/messages.d.mts.map +1 -1
  57. package/resources/messages.d.ts +34 -34
  58. package/resources/messages.d.ts.map +1 -1
  59. package/resources/messages.js +29 -29
  60. package/resources/messages.js.map +1 -1
  61. package/resources/messages.mjs +29 -29
  62. package/resources/messages.mjs.map +1 -1
  63. package/resources/webhook-events.d.mts +83 -44
  64. package/resources/webhook-events.d.mts.map +1 -1
  65. package/resources/webhook-events.d.ts +83 -44
  66. package/resources/webhook-events.d.ts.map +1 -1
  67. package/resources/webhook-events.js +81 -42
  68. package/resources/webhook-events.js.map +1 -1
  69. package/resources/webhook-events.mjs +81 -42
  70. package/resources/webhook-events.mjs.map +1 -1
  71. package/resources/webhook-subscriptions.d.mts +100 -55
  72. package/resources/webhook-subscriptions.d.mts.map +1 -1
  73. package/resources/webhook-subscriptions.d.ts +100 -55
  74. package/resources/webhook-subscriptions.d.ts.map +1 -1
  75. package/resources/webhook-subscriptions.js +103 -58
  76. package/resources/webhook-subscriptions.js.map +1 -1
  77. package/resources/webhook-subscriptions.mjs +103 -58
  78. package/resources/webhook-subscriptions.mjs.map +1 -1
  79. package/resources/webhooks.d.mts +4 -4
  80. package/resources/webhooks.d.mts.map +1 -1
  81. package/resources/webhooks.d.ts +4 -4
  82. package/resources/webhooks.d.ts.map +1 -1
  83. package/src/client.ts +166 -88
  84. package/src/resources/chats/chats.ts +88 -78
  85. package/src/resources/chats/index.ts +3 -2
  86. package/src/resources/chats/location.ts +138 -0
  87. package/src/resources/chats/messages.ts +28 -28
  88. package/src/resources/contact-card.ts +25 -25
  89. package/src/resources/index.ts +4 -4
  90. package/src/resources/messages.ts +46 -46
  91. package/src/resources/webhook-events.ts +85 -44
  92. package/src/resources/webhook-subscriptions.ts +103 -58
  93. package/src/resources/webhooks.ts +6 -4
  94. package/src/version.ts +1 -1
  95. package/version.d.mts +1 -1
  96. package/version.d.ts +1 -1
  97. package/version.js +1 -1
  98. package/version.mjs +1 -1
@@ -15,55 +15,82 @@ import { RequestOptions } from "../internal/request-options.js";
15
15
  *
16
16
  * ## Webhook Headers
17
17
  *
18
- * Each webhook request includes the following headers:
18
+ * All webhook requests include two sets of headers. **If you have an existing integration
19
+ * using the `X-Webhook-*` headers, nothing changes** — those headers are still sent on
20
+ * every delivery and work exactly as before. The new `webhook-*` headers follow the
21
+ * [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks) specification.
22
+ * You can safely ignore them if your current verification code works and you don't want to use this convention.
23
+ *
24
+ * ### Standard Webhooks Headers (Recommended)
25
+ *
26
+ * Used by [our SDK](https://github.com/linq-team/linq-node) and any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks).
19
27
  *
20
28
  * | Header | Description |
21
29
  * |--------|-------------|
22
- * | `X-Webhook-Event` | The event type (e.g., `message.sent`, `message.received`) |
23
- * | `X-Webhook-Subscription-ID` | Your webhook subscription ID |
24
- * | `X-Webhook-Timestamp` | Unix timestamp (seconds) when the webhook was sent |
25
- * | `X-Webhook-Signature` | HMAC-SHA256 signature for verification |
30
+ * | `webhook-id` | Unique event identifier (use as idempotency key) |
31
+ * | `webhook-timestamp` | Unix timestamp (seconds) when the webhook was sent |
32
+ * | `webhook-signature` | Standard Webhooks signature (`v1,{base64}` format) |
26
33
  *
27
- * ## Verifying Webhook Signatures
34
+ * ### Legacy Headers (Deprecated)
35
+ *
36
+ * Still sent on every delivery for backwards compatibility. Existing verification code
37
+ * using these headers continues to work — no changes required.
38
+ *
39
+ * | Header | Description |
40
+ * |--------|-------------|
41
+ * | `X-Webhook-Event` | *(deprecated)* Event type (e.g., `message.sent`) |
42
+ * | `X-Webhook-Subscription-ID` | *(deprecated)* Webhook subscription ID |
43
+ * | `X-Webhook-Timestamp` | *(deprecated)* Unix timestamp (seconds) |
44
+ * | `X-Webhook-Signature` | *(deprecated)* HMAC-SHA256 signature (hex-encoded) |
28
45
  *
29
- * All webhooks are signed using HMAC-SHA256. You should always verify the signature
30
- * to ensure the webhook originated from Linq and hasn't been tampered with.
46
+ * ## Signing Secrets
31
47
  *
32
- * **Signature Construction:**
48
+ * Signing secrets use the Standard Webhooks format: a `whsec_` prefix followed
49
+ * by base64-encoded random bytes (e.g., `whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw7Jxx2Oll+OE=`).
33
50
  *
34
- * The signature is computed over a concatenation of the timestamp and payload:
51
+ * Strip the `whsec_` prefix and base64-decode the remainder to get the raw key bytes.
35
52
  *
36
- * ```
37
- * {timestamp}.{payload}
38
- * ```
53
+ * ## Verifying Webhook Signatures
39
54
  *
40
- * Where:
41
- * - `timestamp` is the value from the `X-Webhook-Timestamp` header
42
- * - `payload` is the raw JSON request body (exact bytes, not re-serialized)
55
+ * Webhooks are signed following the [Standard Webhooks specification](https://github.com/standard-webhooks/standard-webhooks).
56
+ * You can use any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks) to verify
57
+ * signatures, or implement verification manually:
58
+ *
59
+ * **Signed content:** `{webhook-id}.{webhook-timestamp}.{body}`
43
60
  *
44
61
  * **Verification Steps:**
45
62
  *
46
- * 1. Extract the `X-Webhook-Timestamp` and `X-Webhook-Signature` headers
47
- * 2. Get the raw request body bytes (do not parse and re-serialize)
48
- * 3. Concatenate: `"{timestamp}.{payload}"`
49
- * 4. Compute HMAC-SHA256 using your signing secret as the key
50
- * 5. Hex-encode the result and compare with `X-Webhook-Signature`
51
- * 6. Use constant-time comparison to prevent timing attacks
63
+ * 1. Extract the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers
64
+ * 2. Reject if the timestamp is more than 5 minutes old (replay protection)
65
+ * 3. Get the raw request body bytes (do not parse and re-serialize)
66
+ * 4. Construct signed content: `"{webhook-id}.{webhook-timestamp}.{body}"`
67
+ * 5. Strip the `whsec_` prefix from your secret and base64-decode to get key bytes
68
+ * 6. Compute HMAC-SHA256 using the key bytes over the signed content
69
+ * 7. Base64-encode the result and compare with the value after `v1,` in `webhook-signature`
70
+ * 8. Use constant-time comparison to prevent timing attacks
52
71
  *
53
72
  * **Example (Python):**
54
73
  *
55
74
  * ```python
56
- * import hmac
57
- * import hashlib
75
+ * import base64, hmac, hashlib
76
+ *
77
+ * def verify_webhook(secret, body, headers):
78
+ * msg_id = headers['webhook-id']
79
+ * timestamp = headers['webhook-timestamp']
80
+ * signature = headers['webhook-signature']
81
+ *
82
+ * secret_str = secret.removeprefix('whsec_')
83
+ * key = base64.b64decode(secret_str)
58
84
  *
59
- * def verify_webhook(signing_secret, payload, timestamp, signature):
60
- * message = f"{timestamp}.{payload.decode('utf-8')}"
61
- * expected = hmac.new(
62
- * signing_secret.encode('utf-8'),
63
- * message.encode('utf-8'),
64
- * hashlib.sha256
65
- * ).hexdigest()
66
- * return hmac.compare_digest(expected, signature)
85
+ * signed_content = f"{msg_id}.{timestamp}.{body}"
86
+ * expected = base64.b64encode(
87
+ * hmac.new(key, signed_content.encode(), hashlib.sha256).digest()
88
+ * ).decode()
89
+ *
90
+ * for sig in signature.split(' '):
91
+ * if sig.startswith('v1,') and hmac.compare_digest(expected, sig[3:]):
92
+ * return True
93
+ * return False
67
94
  * ```
68
95
  *
69
96
  * **Example (Node.js):**
@@ -71,16 +98,28 @@ import { RequestOptions } from "../internal/request-options.js";
71
98
  * ```javascript
72
99
  * const crypto = require('crypto');
73
100
  *
74
- * function verifyWebhook(signingSecret, payload, timestamp, signature) {
75
- * const message = `${timestamp}.${payload}`;
101
+ * function verifyWebhook(secret, rawBody, headers) {
102
+ * const msgId = headers['webhook-id'];
103
+ * const timestamp = headers['webhook-timestamp'];
104
+ * const signature = headers['webhook-signature'];
105
+ *
106
+ * const secretStr = secret.startsWith('whsec_') ? secret.slice(6) : secret;
107
+ * const keyBytes = Buffer.from(secretStr, 'base64');
108
+ * const signedContent = `${msgId}.${timestamp}.${rawBody}`;
76
109
  * const expected = crypto
77
- * .createHmac('sha256', signingSecret)
78
- * .update(message)
79
- * .digest('hex');
80
- * return crypto.timingSafeEqual(
81
- * Buffer.from(expected),
82
- * Buffer.from(signature)
83
- * );
110
+ * .createHmac('sha256', keyBytes)
111
+ * .update(signedContent)
112
+ * .digest('base64');
113
+ *
114
+ * return signature.split(' ').some(sig => {
115
+ * if (!sig.startsWith('v1,')) return false;
116
+ * try {
117
+ * return crypto.timingSafeEqual(
118
+ * Buffer.from(expected, 'base64'),
119
+ * Buffer.from(sig.slice(3), 'base64')
120
+ * );
121
+ * } catch { return false; }
122
+ * });
84
123
  * }
85
124
  * ```
86
125
  *
@@ -110,9 +149,15 @@ export declare class WebhookSubscriptions extends APIResource {
110
149
  * **Webhook Delivery:**
111
150
  *
112
151
  * - Events are sent via HTTP POST to the target URL
113
- * - Each request includes `X-Webhook-Signature` and `X-Webhook-Timestamp` headers
114
- * - Signature is HMAC-SHA256 over `{timestamp}.{payload}` — see
115
- * [Webhook Events](/docs/webhook-events) for verification details
152
+ * - Each request includes
153
+ * [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks)
154
+ * headers (`webhook-id`, `webhook-timestamp`, `webhook-signature`) for signature
155
+ * verification
156
+ * - Legacy `X-Webhook-*` headers are also sent for backwards compatibility
157
+ * (deprecated)
158
+ * - See
159
+ * [Verifying Webhook Signatures](https://docs.linqapp.com/guides/webhooks#verifying-webhook-signatures)
160
+ * for verification details
116
161
  * - Failed deliveries (5xx, 429, network errors) are retried up to 10 times over
117
162
  * ~25 minutes with exponential backoff
118
163
  * - Client errors (4xx except 429) are not retried
@@ -131,6 +176,17 @@ export declare class WebhookSubscriptions extends APIResource {
131
176
  * ```
132
177
  */
133
178
  create(body: WebhookSubscriptionCreateParams, options?: RequestOptions): APIPromise<WebhookSubscriptionCreateResponse>;
179
+ /**
180
+ * Retrieve all webhook subscriptions for the authenticated partner. Returns a list
181
+ * of active and inactive subscriptions with their configuration and status.
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * const webhookSubscriptions =
186
+ * await client.webhookSubscriptions.list();
187
+ * ```
188
+ */
189
+ list(options?: RequestOptions): APIPromise<WebhookSubscriptionListResponse>;
134
190
  /**
135
191
  * Retrieve details for a specific webhook subscription including its target URL,
136
192
  * subscribed events, and current status.
@@ -163,17 +219,6 @@ export declare class WebhookSubscriptions extends APIResource {
163
219
  * ```
164
220
  */
165
221
  update(subscriptionID: string, body: WebhookSubscriptionUpdateParams, options?: RequestOptions): APIPromise<WebhookSubscription>;
166
- /**
167
- * Retrieve all webhook subscriptions for the authenticated partner. Returns a list
168
- * of active and inactive subscriptions with their configuration and status.
169
- *
170
- * @example
171
- * ```ts
172
- * const webhookSubscriptions =
173
- * await client.webhookSubscriptions.list();
174
- * ```
175
- */
176
- list(options?: RequestOptions): APIPromise<WebhookSubscriptionListResponse>;
177
222
  /**
178
223
  * Delete a webhook subscription.
179
224
  *
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-subscriptions.d.ts","sourceRoot":"","sources":["../src/resources/webhook-subscriptions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webhook-subscriptions.d.ts","sourceRoot":"","sources":["../src/resources/webhook-subscriptions.ts"],"names":[],"mappings":"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"}
@@ -18,55 +18,82 @@ const path_1 = require("../internal/utils/path.js");
18
18
  *
19
19
  * ## Webhook Headers
20
20
  *
21
- * Each webhook request includes the following headers:
21
+ * All webhook requests include two sets of headers. **If you have an existing integration
22
+ * using the `X-Webhook-*` headers, nothing changes** — those headers are still sent on
23
+ * every delivery and work exactly as before. The new `webhook-*` headers follow the
24
+ * [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks) specification.
25
+ * You can safely ignore them if your current verification code works and you don't want to use this convention.
26
+ *
27
+ * ### Standard Webhooks Headers (Recommended)
28
+ *
29
+ * Used by [our SDK](https://github.com/linq-team/linq-node) and any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks).
22
30
  *
23
31
  * | Header | Description |
24
32
  * |--------|-------------|
25
- * | `X-Webhook-Event` | The event type (e.g., `message.sent`, `message.received`) |
26
- * | `X-Webhook-Subscription-ID` | Your webhook subscription ID |
27
- * | `X-Webhook-Timestamp` | Unix timestamp (seconds) when the webhook was sent |
28
- * | `X-Webhook-Signature` | HMAC-SHA256 signature for verification |
33
+ * | `webhook-id` | Unique event identifier (use as idempotency key) |
34
+ * | `webhook-timestamp` | Unix timestamp (seconds) when the webhook was sent |
35
+ * | `webhook-signature` | Standard Webhooks signature (`v1,{base64}` format) |
29
36
  *
30
- * ## Verifying Webhook Signatures
37
+ * ### Legacy Headers (Deprecated)
31
38
  *
32
- * All webhooks are signed using HMAC-SHA256. You should always verify the signature
33
- * to ensure the webhook originated from Linq and hasn't been tampered with.
39
+ * Still sent on every delivery for backwards compatibility. Existing verification code
40
+ * using these headers continues to work no changes required.
34
41
  *
35
- * **Signature Construction:**
42
+ * | Header | Description |
43
+ * |--------|-------------|
44
+ * | `X-Webhook-Event` | *(deprecated)* Event type (e.g., `message.sent`) |
45
+ * | `X-Webhook-Subscription-ID` | *(deprecated)* Webhook subscription ID |
46
+ * | `X-Webhook-Timestamp` | *(deprecated)* Unix timestamp (seconds) |
47
+ * | `X-Webhook-Signature` | *(deprecated)* HMAC-SHA256 signature (hex-encoded) |
36
48
  *
37
- * The signature is computed over a concatenation of the timestamp and payload:
49
+ * ## Signing Secrets
38
50
  *
39
- * ```
40
- * {timestamp}.{payload}
41
- * ```
51
+ * Signing secrets use the Standard Webhooks format: a `whsec_` prefix followed
52
+ * by base64-encoded random bytes (e.g., `whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw7Jxx2Oll+OE=`).
53
+ *
54
+ * Strip the `whsec_` prefix and base64-decode the remainder to get the raw key bytes.
55
+ *
56
+ * ## Verifying Webhook Signatures
57
+ *
58
+ * Webhooks are signed following the [Standard Webhooks specification](https://github.com/standard-webhooks/standard-webhooks).
59
+ * You can use any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks) to verify
60
+ * signatures, or implement verification manually:
42
61
  *
43
- * Where:
44
- * - `timestamp` is the value from the `X-Webhook-Timestamp` header
45
- * - `payload` is the raw JSON request body (exact bytes, not re-serialized)
62
+ * **Signed content:** `{webhook-id}.{webhook-timestamp}.{body}`
46
63
  *
47
64
  * **Verification Steps:**
48
65
  *
49
- * 1. Extract the `X-Webhook-Timestamp` and `X-Webhook-Signature` headers
50
- * 2. Get the raw request body bytes (do not parse and re-serialize)
51
- * 3. Concatenate: `"{timestamp}.{payload}"`
52
- * 4. Compute HMAC-SHA256 using your signing secret as the key
53
- * 5. Hex-encode the result and compare with `X-Webhook-Signature`
54
- * 6. Use constant-time comparison to prevent timing attacks
66
+ * 1. Extract the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers
67
+ * 2. Reject if the timestamp is more than 5 minutes old (replay protection)
68
+ * 3. Get the raw request body bytes (do not parse and re-serialize)
69
+ * 4. Construct signed content: `"{webhook-id}.{webhook-timestamp}.{body}"`
70
+ * 5. Strip the `whsec_` prefix from your secret and base64-decode to get key bytes
71
+ * 6. Compute HMAC-SHA256 using the key bytes over the signed content
72
+ * 7. Base64-encode the result and compare with the value after `v1,` in `webhook-signature`
73
+ * 8. Use constant-time comparison to prevent timing attacks
55
74
  *
56
75
  * **Example (Python):**
57
76
  *
58
77
  * ```python
59
- * import hmac
60
- * import hashlib
61
- *
62
- * def verify_webhook(signing_secret, payload, timestamp, signature):
63
- * message = f"{timestamp}.{payload.decode('utf-8')}"
64
- * expected = hmac.new(
65
- * signing_secret.encode('utf-8'),
66
- * message.encode('utf-8'),
67
- * hashlib.sha256
68
- * ).hexdigest()
69
- * return hmac.compare_digest(expected, signature)
78
+ * import base64, hmac, hashlib
79
+ *
80
+ * def verify_webhook(secret, body, headers):
81
+ * msg_id = headers['webhook-id']
82
+ * timestamp = headers['webhook-timestamp']
83
+ * signature = headers['webhook-signature']
84
+ *
85
+ * secret_str = secret.removeprefix('whsec_')
86
+ * key = base64.b64decode(secret_str)
87
+ *
88
+ * signed_content = f"{msg_id}.{timestamp}.{body}"
89
+ * expected = base64.b64encode(
90
+ * hmac.new(key, signed_content.encode(), hashlib.sha256).digest()
91
+ * ).decode()
92
+ *
93
+ * for sig in signature.split(' '):
94
+ * if sig.startswith('v1,') and hmac.compare_digest(expected, sig[3:]):
95
+ * return True
96
+ * return False
70
97
  * ```
71
98
  *
72
99
  * **Example (Node.js):**
@@ -74,16 +101,28 @@ const path_1 = require("../internal/utils/path.js");
74
101
  * ```javascript
75
102
  * const crypto = require('crypto');
76
103
  *
77
- * function verifyWebhook(signingSecret, payload, timestamp, signature) {
78
- * const message = `${timestamp}.${payload}`;
104
+ * function verifyWebhook(secret, rawBody, headers) {
105
+ * const msgId = headers['webhook-id'];
106
+ * const timestamp = headers['webhook-timestamp'];
107
+ * const signature = headers['webhook-signature'];
108
+ *
109
+ * const secretStr = secret.startsWith('whsec_') ? secret.slice(6) : secret;
110
+ * const keyBytes = Buffer.from(secretStr, 'base64');
111
+ * const signedContent = `${msgId}.${timestamp}.${rawBody}`;
79
112
  * const expected = crypto
80
- * .createHmac('sha256', signingSecret)
81
- * .update(message)
82
- * .digest('hex');
83
- * return crypto.timingSafeEqual(
84
- * Buffer.from(expected),
85
- * Buffer.from(signature)
86
- * );
113
+ * .createHmac('sha256', keyBytes)
114
+ * .update(signedContent)
115
+ * .digest('base64');
116
+ *
117
+ * return signature.split(' ').some(sig => {
118
+ * if (!sig.startsWith('v1,')) return false;
119
+ * try {
120
+ * return crypto.timingSafeEqual(
121
+ * Buffer.from(expected, 'base64'),
122
+ * Buffer.from(sig.slice(3), 'base64')
123
+ * );
124
+ * } catch { return false; }
125
+ * });
87
126
  * }
88
127
  * ```
89
128
  *
@@ -113,9 +152,15 @@ class WebhookSubscriptions extends resource_1.APIResource {
113
152
  * **Webhook Delivery:**
114
153
  *
115
154
  * - Events are sent via HTTP POST to the target URL
116
- * - Each request includes `X-Webhook-Signature` and `X-Webhook-Timestamp` headers
117
- * - Signature is HMAC-SHA256 over `{timestamp}.{payload}` — see
118
- * [Webhook Events](/docs/webhook-events) for verification details
155
+ * - Each request includes
156
+ * [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks)
157
+ * headers (`webhook-id`, `webhook-timestamp`, `webhook-signature`) for signature
158
+ * verification
159
+ * - Legacy `X-Webhook-*` headers are also sent for backwards compatibility
160
+ * (deprecated)
161
+ * - See
162
+ * [Verifying Webhook Signatures](https://docs.linqapp.com/guides/webhooks#verifying-webhook-signatures)
163
+ * for verification details
119
164
  * - Failed deliveries (5xx, 429, network errors) are retried up to 10 times over
120
165
  * ~25 minutes with exponential backoff
121
166
  * - Client errors (4xx except 429) are not retried
@@ -136,6 +181,19 @@ class WebhookSubscriptions extends resource_1.APIResource {
136
181
  create(body, options) {
137
182
  return this._client.post('/v3/webhook-subscriptions', { body, ...options });
138
183
  }
184
+ /**
185
+ * Retrieve all webhook subscriptions for the authenticated partner. Returns a list
186
+ * of active and inactive subscriptions with their configuration and status.
187
+ *
188
+ * @example
189
+ * ```ts
190
+ * const webhookSubscriptions =
191
+ * await client.webhookSubscriptions.list();
192
+ * ```
193
+ */
194
+ list(options) {
195
+ return this._client.get('/v3/webhook-subscriptions', options);
196
+ }
139
197
  /**
140
198
  * Retrieve details for a specific webhook subscription including its target URL,
141
199
  * subscribed events, and current status.
@@ -172,19 +230,6 @@ class WebhookSubscriptions extends resource_1.APIResource {
172
230
  update(subscriptionID, body, options) {
173
231
  return this._client.put((0, path_1.path) `/v3/webhook-subscriptions/${subscriptionID}`, { body, ...options });
174
232
  }
175
- /**
176
- * Retrieve all webhook subscriptions for the authenticated partner. Returns a list
177
- * of active and inactive subscriptions with their configuration and status.
178
- *
179
- * @example
180
- * ```ts
181
- * const webhookSubscriptions =
182
- * await client.webhookSubscriptions.list();
183
- * ```
184
- */
185
- list(options) {
186
- return this._client.get('/v3/webhook-subscriptions', options);
187
- }
188
233
  /**
189
234
  * Delete a webhook subscription.
190
235
  *
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-subscriptions.js","sourceRoot":"","sources":["../src/resources/webhook-subscriptions.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;AAEtF,kDAA+C;AAG/C,oDAAmD;AAEnD,oDAA8C;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AACH,MAAa,oBAAqB,SAAQ,sBAAW;IACnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACH,MAAM,CACJ,IAAqC,EACrC,OAAwB;QAExB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,cAAsB,EAAE,OAAwB;QACvD,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAA,WAAI,EAAA,6BAA6B,cAAc,EAAE,EAAE,OAAO,CAAC,CAAC;IACtF,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CACJ,cAAsB,EACtB,IAAqC,EACrC,OAAwB;QAExB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAA,WAAI,EAAA,6BAA6B,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACnG,CAAC;IAED;;;;;;;;;OASG;IACH,IAAI,CAAC,OAAwB;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,OAAO,CAAC,CAAC;IAChE,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,cAAsB,EAAE,OAAwB;QACrD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAA,WAAI,EAAA,6BAA6B,cAAc,EAAE,EAAE;YAC5E,GAAG,OAAO;YACV,OAAO,EAAE,IAAA,sBAAY,EAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;SAC7D,CAAC,CAAC;IACL,CAAC;CACF;AAtHD,oDAsHC"}
1
+ {"version":3,"file":"webhook-subscriptions.js","sourceRoot":"","sources":["../src/resources/webhook-subscriptions.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;AAEtF,kDAA+C;AAG/C,oDAAmD;AAEnD,oDAA8C;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+HG;AACH,MAAa,oBAAqB,SAAQ,sBAAW;IACnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2CG;IACH,MAAM,CACJ,IAAqC,EACrC,OAAwB;QAExB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;;;;;OASG;IACH,IAAI,CAAC,OAAwB;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,OAAO,CAAC,CAAC;IAChE,CAAC;IAED;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,cAAsB,EAAE,OAAwB;QACvD,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAA,WAAI,EAAA,6BAA6B,cAAc,EAAE,EAAE,OAAO,CAAC,CAAC;IACtF,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CACJ,cAAsB,EACtB,IAAqC,EACrC,OAAwB;QAExB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAA,WAAI,EAAA,6BAA6B,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACnG,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,cAAsB,EAAE,OAAwB;QACrD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAA,WAAI,EAAA,6BAA6B,cAAc,EAAE,EAAE;YAC5E,GAAG,OAAO;YACV,OAAO,EAAE,IAAA,sBAAY,EAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;SAC7D,CAAC,CAAC;IACL,CAAC;CACF;AA5HD,oDA4HC"}