@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
@@ -0,0 +1,138 @@
1
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ import { APIResource } from '../../core/resource';
4
+ import { APIPromise } from '../../core/api-promise';
5
+ import { buildHeaders } from '../../internal/headers';
6
+ import { RequestOptions } from '../../internal/request-options';
7
+ import { path } from '../../internal/utils/path';
8
+
9
+ /**
10
+ * Request and retrieve real-time location data via iMessage.
11
+ *
12
+ * Use these endpoints to request a contact's location, retrieve location data
13
+ * for contacts who are sharing with you, and subscribe to webhooks when someone
14
+ * starts or stops sharing their location.
15
+ *
16
+ * **Coordinates** are returned in [GeoJSON](https://datatracker.ietf.org/doc/html/rfc7946) format:
17
+ * `[longitude, latitude]` or `[longitude, latitude, altitude]` if altitude is available.
18
+ */
19
+ export class Location extends APIResource {
20
+ /**
21
+ * Send a location sharing request to a contact. They will receive an iMessage
22
+ * prompt asking them to share their location.
23
+ *
24
+ * Location requests only work in **1:1 iMessage chats** (Apple limitation).
25
+ * Attempting to request location in a group chat, or in an SMS or RCS chat,
26
+ * returns `409` (Operation not supported on this chat's service type).
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const locationRequestResponse =
31
+ * await client.chats.location.request(
32
+ * '975d0776-bd17-4273-8337-f346b4c661b0',
33
+ * );
34
+ * ```
35
+ */
36
+ request(chatID: string, options?: RequestOptions): APIPromise<LocationRequestResponse> {
37
+ return this._client.post(path`/v3/chats/${chatID}/location/request`, options);
38
+ }
39
+
40
+ /**
41
+ * Retrieve the current location for contacts sharing with you in a chat.
42
+ *
43
+ * Returns a [GeoJSON](https://datatracker.ietf.org/doc/html/rfc7946)
44
+ * `FeatureCollection` with a `Feature` for each participant actively sharing their
45
+ * location.
46
+ *
47
+ * Works for both 1:1 and group chats. In group chats, returns a separate feature
48
+ * for each participant who is sharing. Each feature's `properties.handle`
49
+ * identifies the user.
50
+ *
51
+ * Returns an empty `features` array if no one is sharing or no location data is
52
+ * available yet.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const getChatLocationResponse =
57
+ * await client.chats.location.retrieve(
58
+ * '975d0776-bd17-4273-8337-f346b4c661b0',
59
+ * );
60
+ * ```
61
+ */
62
+ retrieve(chatID: string, options?: RequestOptions): APIPromise<GetChatLocationResponse> {
63
+ return this._client.get(path`/v3/chats/${chatID}/location`, {
64
+ ...options,
65
+ headers: buildHeaders([{ Accept: 'application/geo+json' }, options?.headers]),
66
+ });
67
+ }
68
+ }
69
+
70
+ export interface GetChatLocationResponse {
71
+ data: GetChatLocationResponse.Data;
72
+
73
+ success: boolean;
74
+ }
75
+
76
+ export namespace GetChatLocationResponse {
77
+ export interface Data {
78
+ features: Array<Data.Feature>;
79
+
80
+ type: 'FeatureCollection';
81
+ }
82
+
83
+ export namespace Data {
84
+ export interface Feature {
85
+ geometry: Feature.Geometry;
86
+
87
+ properties: Feature.Properties;
88
+
89
+ type: 'Feature';
90
+ }
91
+
92
+ export namespace Feature {
93
+ export interface Geometry {
94
+ /**
95
+ * [longitude, latitude] or [longitude, latitude, altitude]
96
+ */
97
+ coordinates: Array<number>;
98
+
99
+ type: 'Point';
100
+ }
101
+
102
+ export interface Properties {
103
+ /**
104
+ * Phone number or email of the person sharing their location
105
+ */
106
+ handle: string;
107
+
108
+ /**
109
+ * Full street address
110
+ */
111
+ address?: string;
112
+
113
+ /**
114
+ * City or locality name
115
+ */
116
+ locality?: string;
117
+
118
+ /**
119
+ * When the location was last updated
120
+ */
121
+ updated_at?: string;
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ export interface LocationRequestResponse {
128
+ message: string;
129
+
130
+ success: boolean;
131
+ }
132
+
133
+ export declare namespace Location {
134
+ export {
135
+ type GetChatLocationResponse as GetChatLocationResponse,
136
+ type LocationRequestResponse as LocationRequestResponse,
137
+ };
138
+ }
@@ -35,31 +35,6 @@ import { path } from '../../internal/utils/path';
35
35
  * - Maximum URL length: 2,048 characters.
36
36
  */
37
37
  export class Messages extends APIResource {
38
- /**
39
- * Retrieve messages from a specific chat with pagination support.
40
- *
41
- * @example
42
- * ```ts
43
- * // Automatically fetches more pages as needed.
44
- * for await (const message of client.chats.messages.list(
45
- * '550e8400-e29b-41d4-a716-446655440000',
46
- * )) {
47
- * // ...
48
- * }
49
- * ```
50
- */
51
- list(
52
- chatID: string,
53
- query: MessageListParams | null | undefined = {},
54
- options?: RequestOptions,
55
- ): PagePromise<MessagesListMessagesPagination, MessagesAPI.Message> {
56
- return this._client.getAPIList(
57
- path`/v3/chats/${chatID}/messages`,
58
- ListMessagesPagination<MessagesAPI.Message>,
59
- { query, ...options },
60
- );
61
- }
62
-
63
38
  /**
64
39
  * Send a message to an existing chat. Use this endpoint when you already have a
65
40
  * chat ID and want to send additional messages to it.
@@ -119,6 +94,31 @@ export class Messages extends APIResource {
119
94
  send(chatID: string, body: MessageSendParams, options?: RequestOptions): APIPromise<MessageSendResponse> {
120
95
  return this._client.post(path`/v3/chats/${chatID}/messages`, { body, ...options });
121
96
  }
97
+
98
+ /**
99
+ * Retrieve messages from a specific chat with pagination support.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * // Automatically fetches more pages as needed.
104
+ * for await (const message of client.chats.messages.list(
105
+ * '550e8400-e29b-41d4-a716-446655440000',
106
+ * )) {
107
+ * // ...
108
+ * }
109
+ * ```
110
+ */
111
+ list(
112
+ chatID: string,
113
+ query: MessageListParams | null | undefined = {},
114
+ options?: RequestOptions,
115
+ ): PagePromise<MessagesListMessagesPagination, MessagesAPI.Message> {
116
+ return this._client.getAPIList(
117
+ path`/v3/chats/${chatID}/messages`,
118
+ ListMessagesPagination<MessagesAPI.Message>,
119
+ { query, ...options },
120
+ );
121
+ }
122
122
  }
123
123
 
124
124
  /**
@@ -201,8 +201,6 @@ export interface MessageSendResponse {
201
201
  message: SentMessage;
202
202
  }
203
203
 
204
- export interface MessageListParams extends ListMessagesPaginationParams {}
205
-
206
204
  export interface MessageSendParams {
207
205
  /**
208
206
  * Message content container. Groups all message-related fields together,
@@ -212,12 +210,14 @@ export interface MessageSendParams {
212
210
  message: ChatsAPI.MessageContent;
213
211
  }
214
212
 
213
+ export interface MessageListParams extends ListMessagesPaginationParams {}
214
+
215
215
  export declare namespace Messages {
216
216
  export {
217
217
  type SentMessage as SentMessage,
218
218
  type MessageSendResponse as MessageSendResponse,
219
- type MessageListParams as MessageListParams,
220
219
  type MessageSendParams as MessageSendParams,
220
+ type MessageListParams as MessageListParams,
221
221
  };
222
222
  }
223
223
 
@@ -14,6 +14,22 @@ import { RequestOptions } from '../internal/request-options';
14
14
  * **Sharing behavior:** Sharing may not take effect in every chat due to limitations outside our control. We recommend calling the share endpoint once per day, after the first outbound activity.
15
15
  */
16
16
  export class ContactCard extends APIResource {
17
+ /**
18
+ * Returns the contact card for a specific phone number, or all contact cards for
19
+ * the authenticated partner if no `phone_number` is provided.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const contactCard = await client.contactCard.retrieve();
24
+ * ```
25
+ */
26
+ retrieve(
27
+ query: ContactCardRetrieveParams | null | undefined = {},
28
+ options?: RequestOptions,
29
+ ): APIPromise<ContactCardRetrieveResponse> {
30
+ return this._client.get('/v3/contact_card', { query, ...options });
31
+ }
32
+
17
33
  /**
18
34
  * Creates a contact card for a phone number. This endpoint is intended for
19
35
  * initial, one-time setup only.
@@ -40,22 +56,6 @@ export class ContactCard extends APIResource {
40
56
  return this._client.post('/v3/contact_card', { body, ...options });
41
57
  }
42
58
 
43
- /**
44
- * Returns the contact card for a specific phone number, or all contact cards for
45
- * the authenticated partner if no `phone_number` is provided.
46
- *
47
- * @example
48
- * ```ts
49
- * const contactCard = await client.contactCard.retrieve();
50
- * ```
51
- */
52
- retrieve(
53
- query: ContactCardRetrieveParams | null | undefined = {},
54
- options?: RequestOptions,
55
- ): APIPromise<ContactCardRetrieveResponse> {
56
- return this._client.get('/v3/contact_card', { query, ...options });
57
- }
58
-
59
59
  /**
60
60
  * Partially updates an existing active contact card for a phone number.
61
61
  *
@@ -127,6 +127,14 @@ export namespace ContactCardRetrieveResponse {
127
127
  }
128
128
  }
129
129
 
130
+ export interface ContactCardRetrieveParams {
131
+ /**
132
+ * E.164 phone number to filter by. If omitted, all my cards for the partner are
133
+ * returned.
134
+ */
135
+ phone_number?: string;
136
+ }
137
+
130
138
  export interface ContactCardCreateParams {
131
139
  /**
132
140
  * First name for the contact card. Required.
@@ -150,14 +158,6 @@ export interface ContactCardCreateParams {
150
158
  last_name?: string;
151
159
  }
152
160
 
153
- export interface ContactCardRetrieveParams {
154
- /**
155
- * E.164 phone number to filter by. If omitted, all my cards for the partner are
156
- * returned.
157
- */
158
- phone_number?: string;
159
- }
160
-
161
161
  export interface ContactCardUpdateParams {
162
162
  /**
163
163
  * Query param: E.164 phone number of the contact card to update
@@ -184,8 +184,8 @@ export declare namespace ContactCard {
184
184
  export {
185
185
  type SetContactCard as SetContactCard,
186
186
  type ContactCardRetrieveResponse as ContactCardRetrieveResponse,
187
- type ContactCardCreateParams as ContactCardCreateParams,
188
187
  type ContactCardRetrieveParams as ContactCardRetrieveParams,
188
+ type ContactCardCreateParams as ContactCardCreateParams,
189
189
  type ContactCardUpdateParams as ContactCardUpdateParams,
190
190
  };
191
191
  }
@@ -27,8 +27,8 @@ export {
27
27
  type ChatLeaveChatResponse,
28
28
  type ChatSendVoicememoResponse,
29
29
  type ChatCreateParams,
30
- type ChatUpdateParams,
31
30
  type ChatListChatsParams,
31
+ type ChatUpdateParams,
32
32
  type ChatSendVoicememoParams,
33
33
  type ChatsListChatsPagination,
34
34
  } from './chats/chats';
@@ -36,8 +36,8 @@ export {
36
36
  ContactCard,
37
37
  type SetContactCard,
38
38
  type ContactCardRetrieveResponse,
39
- type ContactCardCreateParams,
40
39
  type ContactCardRetrieveParams,
40
+ type ContactCardCreateParams,
41
41
  type ContactCardUpdateParams,
42
42
  } from './contact-card';
43
43
  export {
@@ -46,9 +46,9 @@ export {
46
46
  type MessageEffect,
47
47
  type ReplyTo,
48
48
  type MessageAddReactionResponse,
49
- type MessageUpdateParams,
50
- type MessageAddReactionParams,
51
49
  type MessageListMessagesThreadParams,
50
+ type MessageAddReactionParams,
51
+ type MessageUpdateParams,
52
52
  type MessagesListMessagesPagination,
53
53
  } from './messages';
54
54
  export { PhoneNumbers, type PhoneNumberListResponse } from './phone-numbers';
@@ -30,36 +30,47 @@ import { path } from '../internal/utils/path';
30
30
  */
31
31
  export class Messages extends APIResource {
32
32
  /**
33
- * Retrieve a specific message by its ID. This endpoint returns the full message
34
- * details including text, attachments, reactions, and metadata.
33
+ * Retrieve all messages in a conversation thread. Given any message ID in the
34
+ * thread, returns the originator message and all replies in chronological order.
35
+ *
36
+ * If the message is not part of a thread, returns just that single message.
37
+ *
38
+ * Supports pagination and configurable ordering.
35
39
  *
36
40
  * @example
37
41
  * ```ts
38
- * const message = await client.messages.retrieve(
42
+ * // Automatically fetches more pages as needed.
43
+ * for await (const message of client.messages.listMessagesThread(
39
44
  * '69a37c7d-af4f-4b5e-af42-e28e98ce873a',
40
- * );
45
+ * )) {
46
+ * // ...
47
+ * }
41
48
  * ```
42
49
  */
43
- retrieve(messageID: string, options?: RequestOptions): APIPromise<Message> {
44
- return this._client.get(path`/v3/messages/${messageID}`, options);
50
+ listMessagesThread(
51
+ messageID: string,
52
+ query: MessageListMessagesThreadParams | null | undefined = {},
53
+ options?: RequestOptions,
54
+ ): PagePromise<MessagesListMessagesPagination, Message> {
55
+ return this._client.getAPIList(path`/v3/messages/${messageID}/thread`, ListMessagesPagination<Message>, {
56
+ query,
57
+ ...options,
58
+ });
45
59
  }
46
60
 
47
61
  /**
48
- * Edit the text content of a specific part of a previously sent message.
49
- *
50
- * **Note:** A message can be edited up to 5 times, and only within 15 minutes of
51
- * when it was originally sent.
62
+ * Retrieve a specific message by its ID. This endpoint returns the full message
63
+ * details including text, attachments, reactions, and metadata.
52
64
  *
53
65
  * @example
54
66
  * ```ts
55
- * const message = await client.messages.update(
67
+ * const message = await client.messages.retrieve(
56
68
  * '69a37c7d-af4f-4b5e-af42-e28e98ce873a',
57
- * { text: 'This is the edited message content' },
58
69
  * );
59
70
  * ```
60
71
  */
61
- update(messageID: string, body: MessageUpdateParams, options?: RequestOptions): APIPromise<Message> {
62
- return this._client.patch(path`/v3/messages/${messageID}`, { body, ...options });
72
+ retrieve(messageID: string, options?: RequestOptions): APIPromise<Message> {
73
+ return this._client.get(path`/v3/messages/${messageID}`, options);
63
74
  }
64
75
 
65
76
  /**
@@ -111,32 +122,21 @@ export class Messages extends APIResource {
111
122
  }
112
123
 
113
124
  /**
114
- * Retrieve all messages in a conversation thread. Given any message ID in the
115
- * thread, returns the originator message and all replies in chronological order.
116
- *
117
- * If the message is not part of a thread, returns just that single message.
125
+ * Edit the text content of a specific part of a previously sent message.
118
126
  *
119
- * Supports pagination and configurable ordering.
127
+ * **Note:** A message can be edited up to 5 times, and only within 15 minutes of
128
+ * when it was originally sent.
120
129
  *
121
130
  * @example
122
131
  * ```ts
123
- * // Automatically fetches more pages as needed.
124
- * for await (const message of client.messages.listMessagesThread(
132
+ * const message = await client.messages.update(
125
133
  * '69a37c7d-af4f-4b5e-af42-e28e98ce873a',
126
- * )) {
127
- * // ...
128
- * }
134
+ * { text: 'This is the edited message content' },
135
+ * );
129
136
  * ```
130
137
  */
131
- listMessagesThread(
132
- messageID: string,
133
- query: MessageListMessagesThreadParams | null | undefined = {},
134
- options?: RequestOptions,
135
- ): PagePromise<MessagesListMessagesPagination, Message> {
136
- return this._client.getAPIList(path`/v3/messages/${messageID}/thread`, ListMessagesPagination<Message>, {
137
- query,
138
- ...options,
139
- });
138
+ update(messageID: string, body: MessageUpdateParams, options?: RequestOptions): APIPromise<Message> {
139
+ return this._client.patch(path`/v3/messages/${messageID}`, { body, ...options });
140
140
  }
141
141
  }
142
142
 
@@ -274,16 +274,11 @@ export interface MessageAddReactionResponse {
274
274
  trace_id?: string;
275
275
  }
276
276
 
277
- export interface MessageUpdateParams {
278
- /**
279
- * New text content for the message part
280
- */
281
- text: string;
282
-
277
+ export interface MessageListMessagesThreadParams extends ListMessagesPaginationParams {
283
278
  /**
284
- * Index of the message part to edit. Defaults to 0.
279
+ * Sort order for messages (asc = oldest first, desc = newest first)
285
280
  */
286
- part_index?: number;
281
+ order?: 'asc' | 'desc';
287
282
  }
288
283
 
289
284
  export interface MessageAddReactionParams {
@@ -312,11 +307,16 @@ export interface MessageAddReactionParams {
312
307
  part_index?: number;
313
308
  }
314
309
 
315
- export interface MessageListMessagesThreadParams extends ListMessagesPaginationParams {
310
+ export interface MessageUpdateParams {
316
311
  /**
317
- * Sort order for messages (asc = oldest first, desc = newest first)
312
+ * New text content for the message part
318
313
  */
319
- order?: 'asc' | 'desc';
314
+ text: string;
315
+
316
+ /**
317
+ * Index of the message part to edit. Defaults to 0.
318
+ */
319
+ part_index?: number;
320
320
  }
321
321
 
322
322
  export declare namespace Messages {
@@ -326,8 +326,8 @@ export declare namespace Messages {
326
326
  type ReplyTo as ReplyTo,
327
327
  type MessageAddReactionResponse as MessageAddReactionResponse,
328
328
  type MessagesListMessagesPagination as MessagesListMessagesPagination,
329
- type MessageUpdateParams as MessageUpdateParams,
330
- type MessageAddReactionParams as MessageAddReactionParams,
331
329
  type MessageListMessagesThreadParams as MessageListMessagesThreadParams,
330
+ type MessageAddReactionParams as MessageAddReactionParams,
331
+ type MessageUpdateParams as MessageUpdateParams,
332
332
  };
333
333
  }
@@ -17,55 +17,82 @@ import { RequestOptions } from '../internal/request-options';
17
17
  *
18
18
  * ## Webhook Headers
19
19
  *
20
- * Each webhook request includes the following headers:
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
- * | `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 |
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
- * ## Verifying Webhook Signatures
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
- * 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.
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
- * **Signature Construction:**
48
+ * ## Signing Secrets
35
49
  *
36
- * The signature is computed over a concatenation of the timestamp and payload:
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
- * 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)
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 `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
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
- * 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)
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(signingSecret, payload, timestamp, signature) {
77
- * const message = `${timestamp}.${payload}`;
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', signingSecret)
80
- * .update(message)
81
- * .digest('hex');
82
- * return crypto.timingSafeEqual(
83
- * Buffer.from(expected),
84
- * Buffer.from(signature)
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://apidocs.linqapp.com/documentation/webhook-events';
186
+ doc_url: 'https://docs.linqapp.com/guides/webhooks/events';
146
187
 
147
188
  /**
148
189
  * List of all available webhook event types