@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
@@ -16,55 +16,82 @@ const resource_1 = require("../core/resource.js");
16
16
  *
17
17
  * ## Webhook Headers
18
18
  *
19
- * Each webhook request includes the following headers:
19
+ * All webhook requests include two sets of headers. **If you have an existing integration
20
+ * using the `X-Webhook-*` headers, nothing changes** — those headers are still sent on
21
+ * every delivery and work exactly as before. The new `webhook-*` headers follow the
22
+ * [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks) specification.
23
+ * You can safely ignore them if your current verification code works and you don't want to use this convention.
24
+ *
25
+ * ### Standard Webhooks Headers (Recommended)
26
+ *
27
+ * Used by [our SDK](https://github.com/linq-team/linq-node) and any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks).
20
28
  *
21
29
  * | Header | Description |
22
30
  * |--------|-------------|
23
- * | `X-Webhook-Event` | The event type (e.g., `message.sent`, `message.received`) |
24
- * | `X-Webhook-Subscription-ID` | Your webhook subscription ID |
25
- * | `X-Webhook-Timestamp` | Unix timestamp (seconds) when the webhook was sent |
26
- * | `X-Webhook-Signature` | HMAC-SHA256 signature for verification |
31
+ * | `webhook-id` | Unique event identifier (use as idempotency key) |
32
+ * | `webhook-timestamp` | Unix timestamp (seconds) when the webhook was sent |
33
+ * | `webhook-signature` | Standard Webhooks signature (`v1,{base64}` format) |
27
34
  *
28
- * ## Verifying Webhook Signatures
35
+ * ### Legacy Headers (Deprecated)
36
+ *
37
+ * Still sent on every delivery for backwards compatibility. Existing verification code
38
+ * using these headers continues to work — no changes required.
29
39
  *
30
- * All webhooks are signed using HMAC-SHA256. You should always verify the signature
31
- * to ensure the webhook originated from Linq and hasn't been tampered with.
40
+ * | Header | Description |
41
+ * |--------|-------------|
42
+ * | `X-Webhook-Event` | *(deprecated)* Event type (e.g., `message.sent`) |
43
+ * | `X-Webhook-Subscription-ID` | *(deprecated)* Webhook subscription ID |
44
+ * | `X-Webhook-Timestamp` | *(deprecated)* Unix timestamp (seconds) |
45
+ * | `X-Webhook-Signature` | *(deprecated)* HMAC-SHA256 signature (hex-encoded) |
32
46
  *
33
- * **Signature Construction:**
47
+ * ## Signing Secrets
34
48
  *
35
- * The signature is computed over a concatenation of the timestamp and payload:
49
+ * Signing secrets use the Standard Webhooks format: a `whsec_` prefix followed
50
+ * by base64-encoded random bytes (e.g., `whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw7Jxx2Oll+OE=`).
36
51
  *
37
- * ```
38
- * {timestamp}.{payload}
39
- * ```
52
+ * Strip the `whsec_` prefix and base64-decode the remainder to get the raw key bytes.
40
53
  *
41
- * Where:
42
- * - `timestamp` is the value from the `X-Webhook-Timestamp` header
43
- * - `payload` is the raw JSON request body (exact bytes, not re-serialized)
54
+ * ## Verifying Webhook Signatures
55
+ *
56
+ * Webhooks are signed following the [Standard Webhooks specification](https://github.com/standard-webhooks/standard-webhooks).
57
+ * You can use any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks) to verify
58
+ * signatures, or implement verification manually:
59
+ *
60
+ * **Signed content:** `{webhook-id}.{webhook-timestamp}.{body}`
44
61
  *
45
62
  * **Verification Steps:**
46
63
  *
47
- * 1. Extract the `X-Webhook-Timestamp` and `X-Webhook-Signature` headers
48
- * 2. Get the raw request body bytes (do not parse and re-serialize)
49
- * 3. Concatenate: `"{timestamp}.{payload}"`
50
- * 4. Compute HMAC-SHA256 using your signing secret as the key
51
- * 5. Hex-encode the result and compare with `X-Webhook-Signature`
52
- * 6. Use constant-time comparison to prevent timing attacks
64
+ * 1. Extract the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers
65
+ * 2. Reject if the timestamp is more than 5 minutes old (replay protection)
66
+ * 3. Get the raw request body bytes (do not parse and re-serialize)
67
+ * 4. Construct signed content: `"{webhook-id}.{webhook-timestamp}.{body}"`
68
+ * 5. Strip the `whsec_` prefix from your secret and base64-decode to get key bytes
69
+ * 6. Compute HMAC-SHA256 using the key bytes over the signed content
70
+ * 7. Base64-encode the result and compare with the value after `v1,` in `webhook-signature`
71
+ * 8. Use constant-time comparison to prevent timing attacks
53
72
  *
54
73
  * **Example (Python):**
55
74
  *
56
75
  * ```python
57
- * import hmac
58
- * import hashlib
59
- *
60
- * def verify_webhook(signing_secret, payload, timestamp, signature):
61
- * message = f"{timestamp}.{payload.decode('utf-8')}"
62
- * expected = hmac.new(
63
- * signing_secret.encode('utf-8'),
64
- * message.encode('utf-8'),
65
- * hashlib.sha256
66
- * ).hexdigest()
67
- * return hmac.compare_digest(expected, signature)
76
+ * import base64, hmac, hashlib
77
+ *
78
+ * def verify_webhook(secret, body, headers):
79
+ * msg_id = headers['webhook-id']
80
+ * timestamp = headers['webhook-timestamp']
81
+ * signature = headers['webhook-signature']
82
+ *
83
+ * secret_str = secret.removeprefix('whsec_')
84
+ * key = base64.b64decode(secret_str)
85
+ *
86
+ * signed_content = f"{msg_id}.{timestamp}.{body}"
87
+ * expected = base64.b64encode(
88
+ * hmac.new(key, signed_content.encode(), hashlib.sha256).digest()
89
+ * ).decode()
90
+ *
91
+ * for sig in signature.split(' '):
92
+ * if sig.startswith('v1,') and hmac.compare_digest(expected, sig[3:]):
93
+ * return True
94
+ * return False
68
95
  * ```
69
96
  *
70
97
  * **Example (Node.js):**
@@ -72,16 +99,28 @@ const resource_1 = require("../core/resource.js");
72
99
  * ```javascript
73
100
  * const crypto = require('crypto');
74
101
  *
75
- * function verifyWebhook(signingSecret, payload, timestamp, signature) {
76
- * const message = `${timestamp}.${payload}`;
102
+ * function verifyWebhook(secret, rawBody, headers) {
103
+ * const msgId = headers['webhook-id'];
104
+ * const timestamp = headers['webhook-timestamp'];
105
+ * const signature = headers['webhook-signature'];
106
+ *
107
+ * const secretStr = secret.startsWith('whsec_') ? secret.slice(6) : secret;
108
+ * const keyBytes = Buffer.from(secretStr, 'base64');
109
+ * const signedContent = `${msgId}.${timestamp}.${rawBody}`;
77
110
  * const expected = crypto
78
- * .createHmac('sha256', signingSecret)
79
- * .update(message)
80
- * .digest('hex');
81
- * return crypto.timingSafeEqual(
82
- * Buffer.from(expected),
83
- * Buffer.from(signature)
84
- * );
111
+ * .createHmac('sha256', keyBytes)
112
+ * .update(signedContent)
113
+ * .digest('base64');
114
+ *
115
+ * return signature.split(' ').some(sig => {
116
+ * if (!sig.startsWith('v1,')) return false;
117
+ * try {
118
+ * return crypto.timingSafeEqual(
119
+ * Buffer.from(expected, 'base64'),
120
+ * Buffer.from(sig.slice(3), 'base64')
121
+ * );
122
+ * } catch { return false; }
123
+ * });
85
124
  * }
86
125
  * ```
87
126
  *
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-events.js","sourceRoot":"","sources":["../src/resources/webhook-events.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;AAEtF,kDAA+C;AAI/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AACH,MAAa,aAAc,SAAQ,sBAAW;IAC5C;;;;OAIG;IACH,IAAI,CAAC,OAAwB;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;CACF;AATD,sCASC"}
1
+ {"version":3,"file":"webhook-events.js","sourceRoot":"","sources":["../src/resources/webhook-events.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;AAEtF,kDAA+C;AAI/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+HG;AACH,MAAa,aAAc,SAAQ,sBAAW;IAC5C;;;;OAIG;IACH,IAAI,CAAC,OAAwB;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;CACF;AATD,sCASC"}
@@ -13,55 +13,82 @@ import { APIResource } from "../core/resource.mjs";
13
13
  *
14
14
  * ## Webhook Headers
15
15
  *
16
- * Each webhook request includes the following headers:
16
+ * All webhook requests include two sets of headers. **If you have an existing integration
17
+ * using the `X-Webhook-*` headers, nothing changes** — those headers are still sent on
18
+ * every delivery and work exactly as before. The new `webhook-*` headers follow the
19
+ * [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks) specification.
20
+ * You can safely ignore them if your current verification code works and you don't want to use this convention.
21
+ *
22
+ * ### Standard Webhooks Headers (Recommended)
23
+ *
24
+ * Used by [our SDK](https://github.com/linq-team/linq-node) and any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks).
17
25
  *
18
26
  * | Header | Description |
19
27
  * |--------|-------------|
20
- * | `X-Webhook-Event` | The event type (e.g., `message.sent`, `message.received`) |
21
- * | `X-Webhook-Subscription-ID` | Your webhook subscription ID |
22
- * | `X-Webhook-Timestamp` | Unix timestamp (seconds) when the webhook was sent |
23
- * | `X-Webhook-Signature` | HMAC-SHA256 signature for verification |
28
+ * | `webhook-id` | Unique event identifier (use as idempotency key) |
29
+ * | `webhook-timestamp` | Unix timestamp (seconds) when the webhook was sent |
30
+ * | `webhook-signature` | Standard Webhooks signature (`v1,{base64}` format) |
24
31
  *
25
- * ## Verifying Webhook Signatures
32
+ * ### Legacy Headers (Deprecated)
33
+ *
34
+ * Still sent on every delivery for backwards compatibility. Existing verification code
35
+ * using these headers continues to work — no changes required.
26
36
  *
27
- * All webhooks are signed using HMAC-SHA256. You should always verify the signature
28
- * to ensure the webhook originated from Linq and hasn't been tampered with.
37
+ * | Header | Description |
38
+ * |--------|-------------|
39
+ * | `X-Webhook-Event` | *(deprecated)* Event type (e.g., `message.sent`) |
40
+ * | `X-Webhook-Subscription-ID` | *(deprecated)* Webhook subscription ID |
41
+ * | `X-Webhook-Timestamp` | *(deprecated)* Unix timestamp (seconds) |
42
+ * | `X-Webhook-Signature` | *(deprecated)* HMAC-SHA256 signature (hex-encoded) |
29
43
  *
30
- * **Signature Construction:**
44
+ * ## Signing Secrets
31
45
  *
32
- * The signature is computed over a concatenation of the timestamp and payload:
46
+ * Signing secrets use the Standard Webhooks format: a `whsec_` prefix followed
47
+ * by base64-encoded random bytes (e.g., `whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw7Jxx2Oll+OE=`).
33
48
  *
34
- * ```
35
- * {timestamp}.{payload}
36
- * ```
49
+ * Strip the `whsec_` prefix and base64-decode the remainder to get the raw key bytes.
37
50
  *
38
- * Where:
39
- * - `timestamp` is the value from the `X-Webhook-Timestamp` header
40
- * - `payload` is the raw JSON request body (exact bytes, not re-serialized)
51
+ * ## Verifying Webhook Signatures
52
+ *
53
+ * Webhooks are signed following the [Standard Webhooks specification](https://github.com/standard-webhooks/standard-webhooks).
54
+ * You can use any [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks) to verify
55
+ * signatures, or implement verification manually:
56
+ *
57
+ * **Signed content:** `{webhook-id}.{webhook-timestamp}.{body}`
41
58
  *
42
59
  * **Verification Steps:**
43
60
  *
44
- * 1. Extract the `X-Webhook-Timestamp` and `X-Webhook-Signature` headers
45
- * 2. Get the raw request body bytes (do not parse and re-serialize)
46
- * 3. Concatenate: `"{timestamp}.{payload}"`
47
- * 4. Compute HMAC-SHA256 using your signing secret as the key
48
- * 5. Hex-encode the result and compare with `X-Webhook-Signature`
49
- * 6. Use constant-time comparison to prevent timing attacks
61
+ * 1. Extract the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers
62
+ * 2. Reject if the timestamp is more than 5 minutes old (replay protection)
63
+ * 3. Get the raw request body bytes (do not parse and re-serialize)
64
+ * 4. Construct signed content: `"{webhook-id}.{webhook-timestamp}.{body}"`
65
+ * 5. Strip the `whsec_` prefix from your secret and base64-decode to get key bytes
66
+ * 6. Compute HMAC-SHA256 using the key bytes over the signed content
67
+ * 7. Base64-encode the result and compare with the value after `v1,` in `webhook-signature`
68
+ * 8. Use constant-time comparison to prevent timing attacks
50
69
  *
51
70
  * **Example (Python):**
52
71
  *
53
72
  * ```python
54
- * import hmac
55
- * import hashlib
56
- *
57
- * def verify_webhook(signing_secret, payload, timestamp, signature):
58
- * message = f"{timestamp}.{payload.decode('utf-8')}"
59
- * expected = hmac.new(
60
- * signing_secret.encode('utf-8'),
61
- * message.encode('utf-8'),
62
- * hashlib.sha256
63
- * ).hexdigest()
64
- * return hmac.compare_digest(expected, signature)
73
+ * import base64, hmac, hashlib
74
+ *
75
+ * def verify_webhook(secret, body, headers):
76
+ * msg_id = headers['webhook-id']
77
+ * timestamp = headers['webhook-timestamp']
78
+ * signature = headers['webhook-signature']
79
+ *
80
+ * secret_str = secret.removeprefix('whsec_')
81
+ * key = base64.b64decode(secret_str)
82
+ *
83
+ * signed_content = f"{msg_id}.{timestamp}.{body}"
84
+ * expected = base64.b64encode(
85
+ * hmac.new(key, signed_content.encode(), hashlib.sha256).digest()
86
+ * ).decode()
87
+ *
88
+ * for sig in signature.split(' '):
89
+ * if sig.startswith('v1,') and hmac.compare_digest(expected, sig[3:]):
90
+ * return True
91
+ * return False
65
92
  * ```
66
93
  *
67
94
  * **Example (Node.js):**
@@ -69,16 +96,28 @@ import { APIResource } from "../core/resource.mjs";
69
96
  * ```javascript
70
97
  * const crypto = require('crypto');
71
98
  *
72
- * function verifyWebhook(signingSecret, payload, timestamp, signature) {
73
- * const message = `${timestamp}.${payload}`;
99
+ * function verifyWebhook(secret, rawBody, headers) {
100
+ * const msgId = headers['webhook-id'];
101
+ * const timestamp = headers['webhook-timestamp'];
102
+ * const signature = headers['webhook-signature'];
103
+ *
104
+ * const secretStr = secret.startsWith('whsec_') ? secret.slice(6) : secret;
105
+ * const keyBytes = Buffer.from(secretStr, 'base64');
106
+ * const signedContent = `${msgId}.${timestamp}.${rawBody}`;
74
107
  * const expected = crypto
75
- * .createHmac('sha256', signingSecret)
76
- * .update(message)
77
- * .digest('hex');
78
- * return crypto.timingSafeEqual(
79
- * Buffer.from(expected),
80
- * Buffer.from(signature)
81
- * );
108
+ * .createHmac('sha256', keyBytes)
109
+ * .update(signedContent)
110
+ * .digest('base64');
111
+ *
112
+ * return signature.split(' ').some(sig => {
113
+ * if (!sig.startsWith('v1,')) return false;
114
+ * try {
115
+ * return crypto.timingSafeEqual(
116
+ * Buffer.from(expected, 'base64'),
117
+ * Buffer.from(sig.slice(3), 'base64')
118
+ * );
119
+ * } catch { return false; }
120
+ * });
82
121
  * }
83
122
  * ```
84
123
  *
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-events.mjs","sourceRoot":"","sources":["../src/resources/webhook-events.ts"],"names":[],"mappings":"AAAA,sFAAsF;AAEtF,OAAO,EAAE,WAAW,EAAE,6BAAyB;AAI/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AACH,MAAM,OAAO,aAAc,SAAQ,WAAW;IAC5C;;;;OAIG;IACH,IAAI,CAAC,OAAwB;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;CACF"}
1
+ {"version":3,"file":"webhook-events.mjs","sourceRoot":"","sources":["../src/resources/webhook-events.ts"],"names":[],"mappings":"AAAA,sFAAsF;AAEtF,OAAO,EAAE,WAAW,EAAE,6BAAyB;AAI/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+HG;AACH,MAAM,OAAO,aAAc,SAAQ,WAAW;IAC5C;;;;OAIG;IACH,IAAI,CAAC,OAAwB;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;CACF"}
@@ -15,55 +15,82 @@ import { RequestOptions } from "../internal/request-options.mjs";
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.mjs";
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.mts","sourceRoot":"","sources":["../src/resources/webhook-subscriptions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webhook-subscriptions.d.mts","sourceRoot":"","sources":["../src/resources/webhook-subscriptions.ts"],"names":[],"mappings":"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"}