@linqapp/sdk 0.1.5 → 0.4.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 (189) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +31 -0
  3. package/client.d.mts +299 -7
  4. package/client.d.mts.map +1 -1
  5. package/client.d.ts +299 -7
  6. package/client.d.ts.map +1 -1
  7. package/client.js +294 -12
  8. package/client.js.map +1 -1
  9. package/client.mjs +294 -12
  10. package/client.mjs.map +1 -1
  11. package/core/pagination.d.mts +69 -0
  12. package/core/pagination.d.mts.map +1 -0
  13. package/core/pagination.d.ts +69 -0
  14. package/core/pagination.d.ts.map +1 -0
  15. package/core/pagination.js +125 -0
  16. package/core/pagination.js.map +1 -0
  17. package/core/pagination.mjs +118 -0
  18. package/core/pagination.mjs.map +1 -0
  19. package/index.d.mts +1 -0
  20. package/index.d.mts.map +1 -1
  21. package/index.d.ts +1 -0
  22. package/index.d.ts.map +1 -1
  23. package/index.js +3 -1
  24. package/index.js.map +1 -1
  25. package/index.mjs +1 -0
  26. package/index.mjs.map +1 -1
  27. package/internal/tslib.js +18 -18
  28. package/internal/utils/query.d.mts +5 -0
  29. package/internal/utils/query.d.mts.map +1 -0
  30. package/internal/utils/query.d.ts +5 -0
  31. package/internal/utils/query.d.ts.map +1 -0
  32. package/internal/utils/query.js +23 -0
  33. package/internal/utils/query.js.map +1 -0
  34. package/internal/utils/query.mjs +20 -0
  35. package/internal/utils/query.mjs.map +1 -0
  36. package/internal/utils.d.mts +1 -0
  37. package/internal/utils.d.ts +1 -0
  38. package/internal/utils.js +1 -0
  39. package/internal/utils.js.map +1 -1
  40. package/internal/utils.mjs +1 -0
  41. package/package.json +11 -1
  42. package/pagination.d.mts +2 -0
  43. package/pagination.d.mts.map +1 -0
  44. package/pagination.d.ts +2 -0
  45. package/pagination.d.ts.map +1 -0
  46. package/pagination.js +6 -0
  47. package/pagination.js.map +1 -0
  48. package/pagination.mjs +2 -0
  49. package/pagination.mjs.map +1 -0
  50. package/resources/attachments.d.mts +64 -0
  51. package/resources/attachments.d.mts.map +1 -1
  52. package/resources/attachments.d.ts +64 -0
  53. package/resources/attachments.d.ts.map +1 -1
  54. package/resources/attachments.js +64 -0
  55. package/resources/attachments.js.map +1 -1
  56. package/resources/attachments.mjs +64 -0
  57. package/resources/attachments.mjs.map +1 -1
  58. package/resources/capability.d.mts +12 -9
  59. package/resources/capability.d.mts.map +1 -1
  60. package/resources/capability.d.ts +12 -9
  61. package/resources/capability.d.ts.map +1 -1
  62. package/resources/capability.js +7 -4
  63. package/resources/capability.js.map +1 -1
  64. package/resources/capability.mjs +7 -4
  65. package/resources/capability.mjs.map +1 -1
  66. package/resources/chats/chats.d.mts +76 -97
  67. package/resources/chats/chats.d.mts.map +1 -1
  68. package/resources/chats/chats.d.ts +76 -97
  69. package/resources/chats/chats.d.ts.map +1 -1
  70. package/resources/chats/chats.js +8 -4
  71. package/resources/chats/chats.js.map +1 -1
  72. package/resources/chats/chats.mjs +9 -5
  73. package/resources/chats/chats.mjs.map +1 -1
  74. package/resources/chats/index.d.mts +2 -2
  75. package/resources/chats/index.d.mts.map +1 -1
  76. package/resources/chats/index.d.ts +2 -2
  77. package/resources/chats/index.d.ts.map +1 -1
  78. package/resources/chats/index.js.map +1 -1
  79. package/resources/chats/index.mjs.map +1 -1
  80. package/resources/chats/messages.d.mts +21 -25
  81. package/resources/chats/messages.d.mts.map +1 -1
  82. package/resources/chats/messages.d.ts +21 -25
  83. package/resources/chats/messages.d.ts.map +1 -1
  84. package/resources/chats/messages.js +16 -3
  85. package/resources/chats/messages.js.map +1 -1
  86. package/resources/chats/messages.mjs +16 -3
  87. package/resources/chats/messages.mjs.map +1 -1
  88. package/resources/chats/participants.d.mts +18 -0
  89. package/resources/chats/participants.d.mts.map +1 -1
  90. package/resources/chats/participants.d.ts +18 -0
  91. package/resources/chats/participants.d.ts.map +1 -1
  92. package/resources/chats/participants.js +18 -0
  93. package/resources/chats/participants.js.map +1 -1
  94. package/resources/chats/participants.mjs +18 -0
  95. package/resources/chats/participants.mjs.map +1 -1
  96. package/resources/chats/typing.d.mts +18 -0
  97. package/resources/chats/typing.d.mts.map +1 -1
  98. package/resources/chats/typing.d.ts +18 -0
  99. package/resources/chats/typing.d.ts.map +1 -1
  100. package/resources/chats/typing.js +18 -0
  101. package/resources/chats/typing.js.map +1 -1
  102. package/resources/chats/typing.mjs +18 -0
  103. package/resources/chats/typing.mjs.map +1 -1
  104. package/resources/index.d.mts +4 -3
  105. package/resources/index.d.mts.map +1 -1
  106. package/resources/index.d.ts +4 -3
  107. package/resources/index.d.ts.map +1 -1
  108. package/resources/index.js +3 -1
  109. package/resources/index.js.map +1 -1
  110. package/resources/index.mjs +1 -0
  111. package/resources/index.mjs.map +1 -1
  112. package/resources/messages.d.mts +37 -72
  113. package/resources/messages.d.mts.map +1 -1
  114. package/resources/messages.d.ts +37 -72
  115. package/resources/messages.d.ts.map +1 -1
  116. package/resources/messages.js +34 -4
  117. package/resources/messages.js.map +1 -1
  118. package/resources/messages.mjs +34 -4
  119. package/resources/messages.mjs.map +1 -1
  120. package/resources/phone-numbers.d.mts +9 -0
  121. package/resources/phone-numbers.d.mts.map +1 -1
  122. package/resources/phone-numbers.d.ts +9 -0
  123. package/resources/phone-numbers.d.ts.map +1 -1
  124. package/resources/phone-numbers.js +9 -0
  125. package/resources/phone-numbers.js.map +1 -1
  126. package/resources/phone-numbers.mjs +9 -0
  127. package/resources/phone-numbers.mjs.map +1 -1
  128. package/resources/phonenumbers.d.mts +9 -0
  129. package/resources/phonenumbers.d.mts.map +1 -1
  130. package/resources/phonenumbers.d.ts +9 -0
  131. package/resources/phonenumbers.d.ts.map +1 -1
  132. package/resources/phonenumbers.js +9 -0
  133. package/resources/phonenumbers.js.map +1 -1
  134. package/resources/phonenumbers.mjs +9 -0
  135. package/resources/phonenumbers.mjs.map +1 -1
  136. package/resources/shared.d.mts +51 -0
  137. package/resources/shared.d.mts.map +1 -1
  138. package/resources/shared.d.ts +51 -0
  139. package/resources/shared.d.ts.map +1 -1
  140. package/resources/webhook-events.d.mts +90 -1
  141. package/resources/webhook-events.d.mts.map +1 -1
  142. package/resources/webhook-events.d.ts +90 -1
  143. package/resources/webhook-events.d.ts.map +1 -1
  144. package/resources/webhook-events.js +89 -0
  145. package/resources/webhook-events.js.map +1 -1
  146. package/resources/webhook-events.mjs +89 -0
  147. package/resources/webhook-events.mjs.map +1 -1
  148. package/resources/webhook-subscriptions.d.mts +89 -0
  149. package/resources/webhook-subscriptions.d.mts.map +1 -1
  150. package/resources/webhook-subscriptions.d.ts +89 -0
  151. package/resources/webhook-subscriptions.d.ts.map +1 -1
  152. package/resources/webhook-subscriptions.js +89 -0
  153. package/resources/webhook-subscriptions.js.map +1 -1
  154. package/resources/webhook-subscriptions.mjs +89 -0
  155. package/resources/webhook-subscriptions.mjs.map +1 -1
  156. package/resources/webhooks.d.mts +2506 -0
  157. package/resources/webhooks.d.mts.map +1 -0
  158. package/resources/webhooks.d.ts +2506 -0
  159. package/resources/webhooks.d.ts.map +1 -0
  160. package/resources/webhooks.js +12 -0
  161. package/resources/webhooks.js.map +1 -0
  162. package/resources/webhooks.mjs +8 -0
  163. package/resources/webhooks.mjs.map +1 -0
  164. package/src/client.ts +443 -37
  165. package/src/core/pagination.ts +212 -0
  166. package/src/index.ts +1 -0
  167. package/src/internal/utils/query.ts +23 -0
  168. package/src/internal/utils.ts +1 -0
  169. package/src/pagination.ts +2 -0
  170. package/src/resources/attachments.ts +64 -0
  171. package/src/resources/capability.ts +17 -14
  172. package/src/resources/chats/chats.ts +86 -115
  173. package/src/resources/chats/index.ts +4 -3
  174. package/src/resources/chats/messages.ts +30 -30
  175. package/src/resources/chats/participants.ts +18 -0
  176. package/src/resources/chats/typing.ts +18 -0
  177. package/src/resources/index.ts +55 -10
  178. package/src/resources/messages.ts +49 -90
  179. package/src/resources/phone-numbers.ts +9 -0
  180. package/src/resources/phonenumbers.ts +9 -0
  181. package/src/resources/shared.ts +62 -0
  182. package/src/resources/webhook-events.ts +90 -0
  183. package/src/resources/webhook-subscriptions.ts +89 -0
  184. package/src/resources/webhooks.ts +3089 -0
  185. package/src/version.ts +1 -1
  186. package/version.d.mts +1 -1
  187. package/version.d.ts +1 -1
  188. package/version.js +1 -1
  189. package/version.mjs +1 -1
@@ -3,10 +3,20 @@
3
3
  import { APIResource } from '../core/resource';
4
4
  import * as Shared from './shared';
5
5
  import { APIPromise } from '../core/api-promise';
6
+ import { ListMessagesPagination, type ListMessagesPaginationParams, PagePromise } from '../core/pagination';
6
7
  import { buildHeaders } from '../internal/headers';
7
8
  import { RequestOptions } from '../internal/request-options';
8
9
  import { path } from '../internal/utils/path';
9
10
 
11
+ /**
12
+ * Messages are individual text or multimedia communications within a chat thread.
13
+ *
14
+ * Messages can include text, attachments, special effects (like confetti or fireworks),
15
+ * and reactions. All messages are associated with a specific chat and sent from a
16
+ * phone number you own.
17
+ *
18
+ * Messages support delivery status tracking, read receipts, and editing capabilities.
19
+ */
10
20
  export class Messages extends APIResource {
11
21
  /**
12
22
  * Retrieve a specific message by its ID. This endpoint returns the full message
@@ -23,6 +33,21 @@ export class Messages extends APIResource {
23
33
  return this._client.get(path`/v3/messages/${messageID}`, options);
24
34
  }
25
35
 
36
+ /**
37
+ * Edit the text content of a specific part of a previously sent message.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const message = await client.messages.update(
42
+ * '69a37c7d-af4f-4b5e-af42-e28e98ce873a',
43
+ * { text: 'This is the edited message content' },
44
+ * );
45
+ * ```
46
+ */
47
+ update(messageID: string, body: MessageUpdateParams, options?: RequestOptions): APIPromise<Message> {
48
+ return this._client.patch(path`/v3/messages/${messageID}`, { body, ...options });
49
+ }
50
+
26
51
  /**
27
52
  * Deletes a message from the Linq API only. This does NOT unsend or remove the
28
53
  * message from the actual chat - recipients will still see the message.
@@ -86,20 +111,28 @@ export class Messages extends APIResource {
86
111
  *
87
112
  * @example
88
113
  * ```ts
89
- * const response = await client.messages.retrieveThread(
114
+ * // Automatically fetches more pages as needed.
115
+ * for await (const message of client.messages.listMessagesThread(
90
116
  * '69a37c7d-af4f-4b5e-af42-e28e98ce873a',
91
- * );
117
+ * )) {
118
+ * // ...
119
+ * }
92
120
  * ```
93
121
  */
94
- retrieveThread(
122
+ listMessagesThread(
95
123
  messageID: string,
96
- query: MessageRetrieveThreadParams | null | undefined = {},
124
+ query: MessageListMessagesThreadParams | null | undefined = {},
97
125
  options?: RequestOptions,
98
- ): APIPromise<MessageRetrieveThreadResponse> {
99
- return this._client.get(path`/v3/messages/${messageID}/thread`, { query, ...options });
126
+ ): PagePromise<MessagesListMessagesPagination, Message> {
127
+ return this._client.getAPIList(path`/v3/messages/${messageID}/thread`, ListMessagesPagination<Message>, {
128
+ query,
129
+ ...options,
130
+ });
100
131
  }
101
132
  }
102
133
 
134
+ export type MessagesListMessagesPagination = ListMessagesPagination<Message>;
135
+
103
136
  export interface ChatHandle {
104
137
  /**
105
138
  * Unique identifier for this handle
@@ -137,46 +170,6 @@ export interface ChatHandle {
137
170
  status?: 'active' | 'left' | 'removed' | null;
138
171
  }
139
172
 
140
- /**
141
- * A media attachment part
142
- */
143
- export interface MediaPart {
144
- /**
145
- * Unique attachment identifier
146
- */
147
- id: string;
148
-
149
- /**
150
- * Original filename
151
- */
152
- filename: string;
153
-
154
- /**
155
- * MIME type of the file
156
- */
157
- mime_type: string;
158
-
159
- /**
160
- * Reactions on this message part
161
- */
162
- reactions: Array<Reaction> | null;
163
-
164
- /**
165
- * File size in bytes
166
- */
167
- size_bytes: number;
168
-
169
- /**
170
- * Indicates this is a media attachment part
171
- */
172
- type: 'media';
173
-
174
- /**
175
- * Presigned URL for downloading the attachment (expires in 1 hour).
176
- */
177
- url: string;
178
- }
179
-
180
173
  export interface Message {
181
174
  /**
182
175
  * Unique identifier for the message
@@ -237,7 +230,7 @@ export interface Message {
237
230
  /**
238
231
  * Message parts in order (text and media)
239
232
  */
240
- parts?: Array<TextPart | MediaPart> | null;
233
+ parts?: Array<Shared.TextPartResponse | Shared.MediaPartResponse> | null;
241
234
 
242
235
  /**
243
236
  * Messaging service type
@@ -378,26 +371,6 @@ export interface ReplyTo {
378
371
  part_index?: number;
379
372
  }
380
373
 
381
- /**
382
- * A text message part
383
- */
384
- export interface TextPart {
385
- /**
386
- * Reactions on this message part
387
- */
388
- reactions: Array<Reaction> | null;
389
-
390
- /**
391
- * Indicates this is a text message part
392
- */
393
- type: 'text';
394
-
395
- /**
396
- * The text content
397
- */
398
- value: string;
399
- }
400
-
401
374
  export interface MessageAddReactionResponse {
402
375
  message?: string;
403
376
 
@@ -406,19 +379,16 @@ export interface MessageAddReactionResponse {
406
379
  trace_id?: string;
407
380
  }
408
381
 
409
- /**
410
- * Response containing messages in a thread with pagination
411
- */
412
- export interface MessageRetrieveThreadResponse {
382
+ export interface MessageUpdateParams {
413
383
  /**
414
- * Messages in the thread, ordered by the specified order parameter
384
+ * New text content for the message part
415
385
  */
416
- messages: Array<Message>;
386
+ text: string;
417
387
 
418
388
  /**
419
- * Cursor for fetching the next page of results (null if no more results)
389
+ * Index of the message part to edit. Defaults to 0.
420
390
  */
421
- next_cursor?: string | null;
391
+ part_index?: number;
422
392
  }
423
393
 
424
394
  export interface MessageDeleteParams {
@@ -454,17 +424,7 @@ export interface MessageAddReactionParams {
454
424
  part_index?: number;
455
425
  }
456
426
 
457
- export interface MessageRetrieveThreadParams {
458
- /**
459
- * Pagination cursor from previous next_cursor response
460
- */
461
- cursor?: string;
462
-
463
- /**
464
- * Maximum number of messages to return
465
- */
466
- limit?: number;
467
-
427
+ export interface MessageListMessagesThreadParams extends ListMessagesPaginationParams {
468
428
  /**
469
429
  * Sort order for messages (asc = oldest first, desc = newest first)
470
430
  */
@@ -474,17 +434,16 @@ export interface MessageRetrieveThreadParams {
474
434
  export declare namespace Messages {
475
435
  export {
476
436
  type ChatHandle as ChatHandle,
477
- type MediaPart as MediaPart,
478
437
  type Message as Message,
479
438
  type MessageEffect as MessageEffect,
480
439
  type Reaction as Reaction,
481
440
  type ReactionType as ReactionType,
482
441
  type ReplyTo as ReplyTo,
483
- type TextPart as TextPart,
484
442
  type MessageAddReactionResponse as MessageAddReactionResponse,
485
- type MessageRetrieveThreadResponse as MessageRetrieveThreadResponse,
443
+ type MessagesListMessagesPagination as MessagesListMessagesPagination,
444
+ type MessageUpdateParams as MessageUpdateParams,
486
445
  type MessageDeleteParams as MessageDeleteParams,
487
446
  type MessageAddReactionParams as MessageAddReactionParams,
488
- type MessageRetrieveThreadParams as MessageRetrieveThreadParams,
447
+ type MessageListMessagesThreadParams as MessageListMessagesThreadParams,
489
448
  };
490
449
  }
@@ -4,6 +4,15 @@ import { APIResource } from '../core/resource';
4
4
  import { APIPromise } from '../core/api-promise';
5
5
  import { RequestOptions } from '../internal/request-options';
6
6
 
7
+ /**
8
+ * Phone Numbers represent the phone numbers assigned to your partner account.
9
+ *
10
+ * Use the list phone numbers endpoint to discover which phone numbers are available
11
+ * for sending messages.
12
+ *
13
+ * When creating chats, listing chats, or sending a voice memo, use one of your assigned phone numbers
14
+ * in the `from` field.
15
+ */
7
16
  export class PhoneNumbers extends APIResource {
8
17
  /**
9
18
  * Returns all phone numbers assigned to the authenticated partner. Use this
@@ -4,6 +4,15 @@ import { APIResource } from '../core/resource';
4
4
  import { APIPromise } from '../core/api-promise';
5
5
  import { RequestOptions } from '../internal/request-options';
6
6
 
7
+ /**
8
+ * Phone Numbers represent the phone numbers assigned to your partner account.
9
+ *
10
+ * Use the list phone numbers endpoint to discover which phone numbers are available
11
+ * for sending messages.
12
+ *
13
+ * When creating chats, listing chats, or sending a voice memo, use one of your assigned phone numbers
14
+ * in the `from` field.
15
+ */
7
16
  export class Phonenumbers extends APIResource {
8
17
  /**
9
18
  * **Deprecated.** Use `GET /v3/phone_numbers` instead.
@@ -1,6 +1,68 @@
1
1
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
+ import * as MessagesAPI from './messages';
4
+
5
+ /**
6
+ * A media attachment part
7
+ */
8
+ export interface MediaPartResponse {
9
+ /**
10
+ * Unique attachment identifier
11
+ */
12
+ id: string;
13
+
14
+ /**
15
+ * Original filename
16
+ */
17
+ filename: string;
18
+
19
+ /**
20
+ * MIME type of the file
21
+ */
22
+ mime_type: string;
23
+
24
+ /**
25
+ * Reactions on this message part
26
+ */
27
+ reactions: Array<MessagesAPI.Reaction> | null;
28
+
29
+ /**
30
+ * File size in bytes
31
+ */
32
+ size_bytes: number;
33
+
34
+ /**
35
+ * Indicates this is a media attachment part
36
+ */
37
+ type: 'media';
38
+
39
+ /**
40
+ * Presigned URL for downloading the attachment (expires in 1 hour).
41
+ */
42
+ url: string;
43
+ }
44
+
3
45
  /**
4
46
  * Messaging service type
5
47
  */
6
48
  export type ServiceType = 'iMessage' | 'SMS' | 'RCS';
49
+
50
+ /**
51
+ * A text message part
52
+ */
53
+ export interface TextPartResponse {
54
+ /**
55
+ * Reactions on this message part
56
+ */
57
+ reactions: Array<MessagesAPI.Reaction> | null;
58
+
59
+ /**
60
+ * Indicates this is a text message part
61
+ */
62
+ type: 'text';
63
+
64
+ /**
65
+ * The text content
66
+ */
67
+ value: string;
68
+ }
@@ -4,6 +4,95 @@ import { APIResource } from '../core/resource';
4
4
  import { APIPromise } from '../core/api-promise';
5
5
  import { RequestOptions } from '../internal/request-options';
6
6
 
7
+ /**
8
+ * Webhook Subscriptions allow you to receive real-time notifications when events
9
+ * occur on your account.
10
+ *
11
+ * Configure webhook endpoints to receive events such as messages sent/received,
12
+ * delivery status changes, reactions, typing indicators, and more.
13
+ *
14
+ * Failed deliveries (5xx, 429, network errors) are retried up to 10 times over
15
+ * ~2 hours with exponential backoff. Each event includes a unique ID for
16
+ * deduplication.
17
+ *
18
+ * ## Webhook Headers
19
+ *
20
+ * Each webhook request includes the following headers:
21
+ *
22
+ * | Header | Description |
23
+ * |--------|-------------|
24
+ * | `X-Webhook-Event` | The event type (e.g., `message.sent`, `message.received`) |
25
+ * | `X-Webhook-Subscription-ID` | Your webhook subscription ID |
26
+ * | `X-Webhook-Timestamp` | Unix timestamp (seconds) when the webhook was sent |
27
+ * | `X-Webhook-Signature` | HMAC-SHA256 signature for verification |
28
+ *
29
+ * ## Verifying Webhook Signatures
30
+ *
31
+ * All webhooks are signed using HMAC-SHA256. You should always verify the signature
32
+ * to ensure the webhook originated from Linq and hasn't been tampered with.
33
+ *
34
+ * **Signature Construction:**
35
+ *
36
+ * The signature is computed over a concatenation of the timestamp and payload:
37
+ *
38
+ * ```
39
+ * {timestamp}.{payload}
40
+ * ```
41
+ *
42
+ * Where:
43
+ * - `timestamp` is the value from the `X-Webhook-Timestamp` header
44
+ * - `payload` is the raw JSON request body (exact bytes, not re-serialized)
45
+ *
46
+ * **Verification Steps:**
47
+ *
48
+ * 1. Extract the `X-Webhook-Timestamp` and `X-Webhook-Signature` headers
49
+ * 2. Get the raw request body bytes (do not parse and re-serialize)
50
+ * 3. Concatenate: `"{timestamp}.{payload}"`
51
+ * 4. Compute HMAC-SHA256 using your signing secret as the key
52
+ * 5. Hex-encode the result and compare with `X-Webhook-Signature`
53
+ * 6. Use constant-time comparison to prevent timing attacks
54
+ *
55
+ * **Example (Python):**
56
+ *
57
+ * ```python
58
+ * import hmac
59
+ * import hashlib
60
+ *
61
+ * def verify_webhook(signing_secret, payload, timestamp, signature):
62
+ * message = f"{timestamp}.{payload.decode('utf-8')}"
63
+ * expected = hmac.new(
64
+ * signing_secret.encode('utf-8'),
65
+ * message.encode('utf-8'),
66
+ * hashlib.sha256
67
+ * ).hexdigest()
68
+ * return hmac.compare_digest(expected, signature)
69
+ * ```
70
+ *
71
+ * **Example (Node.js):**
72
+ *
73
+ * ```javascript
74
+ * const crypto = require('crypto');
75
+ *
76
+ * function verifyWebhook(signingSecret, payload, timestamp, signature) {
77
+ * const message = `${timestamp}.${payload}`;
78
+ * const expected = crypto
79
+ * .createHmac('sha256', signingSecret)
80
+ * .update(message)
81
+ * .digest('hex');
82
+ * return crypto.timingSafeEqual(
83
+ * Buffer.from(expected),
84
+ * Buffer.from(signature)
85
+ * );
86
+ * }
87
+ * ```
88
+ *
89
+ * **Security Best Practices:**
90
+ *
91
+ * - Reject webhooks with timestamps older than 5 minutes to prevent replay attacks
92
+ * - Always use constant-time comparison for signature verification
93
+ * - Store your signing secret securely (e.g., environment variable, secrets manager)
94
+ * - Return a 2xx status code quickly, then process the webhook asynchronously
95
+ */
7
96
  export class WebhookEvents extends APIResource {
8
97
  /**
9
98
  * Returns all available webhook event types that can be subscribed to. Use this
@@ -24,6 +113,7 @@ export type WebhookEventType =
24
113
  | 'message.read'
25
114
  | 'message.delivered'
26
115
  | 'message.failed'
116
+ | 'message.edited'
27
117
  | 'reaction.added'
28
118
  | 'reaction.removed'
29
119
  | 'participant.added'
@@ -7,6 +7,95 @@ import { buildHeaders } from '../internal/headers';
7
7
  import { RequestOptions } from '../internal/request-options';
8
8
  import { path } from '../internal/utils/path';
9
9
 
10
+ /**
11
+ * Webhook Subscriptions allow you to receive real-time notifications when events
12
+ * occur on your account.
13
+ *
14
+ * Configure webhook endpoints to receive events such as messages sent/received,
15
+ * delivery status changes, reactions, typing indicators, and more.
16
+ *
17
+ * Failed deliveries (5xx, 429, network errors) are retried up to 10 times over
18
+ * ~2 hours with exponential backoff. Each event includes a unique ID for
19
+ * deduplication.
20
+ *
21
+ * ## Webhook Headers
22
+ *
23
+ * Each webhook request includes the following headers:
24
+ *
25
+ * | Header | Description |
26
+ * |--------|-------------|
27
+ * | `X-Webhook-Event` | The event type (e.g., `message.sent`, `message.received`) |
28
+ * | `X-Webhook-Subscription-ID` | Your webhook subscription ID |
29
+ * | `X-Webhook-Timestamp` | Unix timestamp (seconds) when the webhook was sent |
30
+ * | `X-Webhook-Signature` | HMAC-SHA256 signature for verification |
31
+ *
32
+ * ## Verifying Webhook Signatures
33
+ *
34
+ * All webhooks are signed using HMAC-SHA256. You should always verify the signature
35
+ * to ensure the webhook originated from Linq and hasn't been tampered with.
36
+ *
37
+ * **Signature Construction:**
38
+ *
39
+ * The signature is computed over a concatenation of the timestamp and payload:
40
+ *
41
+ * ```
42
+ * {timestamp}.{payload}
43
+ * ```
44
+ *
45
+ * Where:
46
+ * - `timestamp` is the value from the `X-Webhook-Timestamp` header
47
+ * - `payload` is the raw JSON request body (exact bytes, not re-serialized)
48
+ *
49
+ * **Verification Steps:**
50
+ *
51
+ * 1. Extract the `X-Webhook-Timestamp` and `X-Webhook-Signature` headers
52
+ * 2. Get the raw request body bytes (do not parse and re-serialize)
53
+ * 3. Concatenate: `"{timestamp}.{payload}"`
54
+ * 4. Compute HMAC-SHA256 using your signing secret as the key
55
+ * 5. Hex-encode the result and compare with `X-Webhook-Signature`
56
+ * 6. Use constant-time comparison to prevent timing attacks
57
+ *
58
+ * **Example (Python):**
59
+ *
60
+ * ```python
61
+ * import hmac
62
+ * import hashlib
63
+ *
64
+ * def verify_webhook(signing_secret, payload, timestamp, signature):
65
+ * message = f"{timestamp}.{payload.decode('utf-8')}"
66
+ * expected = hmac.new(
67
+ * signing_secret.encode('utf-8'),
68
+ * message.encode('utf-8'),
69
+ * hashlib.sha256
70
+ * ).hexdigest()
71
+ * return hmac.compare_digest(expected, signature)
72
+ * ```
73
+ *
74
+ * **Example (Node.js):**
75
+ *
76
+ * ```javascript
77
+ * const crypto = require('crypto');
78
+ *
79
+ * function verifyWebhook(signingSecret, payload, timestamp, signature) {
80
+ * const message = `${timestamp}.${payload}`;
81
+ * const expected = crypto
82
+ * .createHmac('sha256', signingSecret)
83
+ * .update(message)
84
+ * .digest('hex');
85
+ * return crypto.timingSafeEqual(
86
+ * Buffer.from(expected),
87
+ * Buffer.from(signature)
88
+ * );
89
+ * }
90
+ * ```
91
+ *
92
+ * **Security Best Practices:**
93
+ *
94
+ * - Reject webhooks with timestamps older than 5 minutes to prevent replay attacks
95
+ * - Always use constant-time comparison for signature verification
96
+ * - Store your signing secret securely (e.g., environment variable, secrets manager)
97
+ * - Return a 2xx status code quickly, then process the webhook asynchronously
98
+ */
10
99
  export class WebhookSubscriptions extends APIResource {
11
100
  /**
12
101
  * Create a new webhook subscription to receive events at a target URL. Upon