linq-python 0.1.0__py3-none-any.whl

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 (139) hide show
  1. linq/__init__.py +102 -0
  2. linq/_base_client.py +2149 -0
  3. linq/_client.py +2479 -0
  4. linq/_compat.py +226 -0
  5. linq/_constants.py +14 -0
  6. linq/_exceptions.py +108 -0
  7. linq/_files.py +123 -0
  8. linq/_models.py +878 -0
  9. linq/_qs.py +153 -0
  10. linq/_resource.py +43 -0
  11. linq/_response.py +833 -0
  12. linq/_streaming.py +338 -0
  13. linq/_types.py +271 -0
  14. linq/_utils/__init__.py +65 -0
  15. linq/_utils/_compat.py +45 -0
  16. linq/_utils/_datetime_parse.py +136 -0
  17. linq/_utils/_json.py +35 -0
  18. linq/_utils/_logs.py +25 -0
  19. linq/_utils/_path.py +127 -0
  20. linq/_utils/_proxy.py +65 -0
  21. linq/_utils/_reflection.py +42 -0
  22. linq/_utils/_resources_proxy.py +24 -0
  23. linq/_utils/_streams.py +12 -0
  24. linq/_utils/_sync.py +58 -0
  25. linq/_utils/_transform.py +457 -0
  26. linq/_utils/_typing.py +156 -0
  27. linq/_utils/_utils.py +421 -0
  28. linq/_version.py +4 -0
  29. linq/lib/.keep +4 -0
  30. linq/pagination.py +95 -0
  31. linq/py.typed +0 -0
  32. linq/resources/__init__.py +134 -0
  33. linq/resources/attachments.py +589 -0
  34. linq/resources/capability.py +297 -0
  35. linq/resources/chats/__init__.py +61 -0
  36. linq/resources/chats/chats.py +1492 -0
  37. linq/resources/chats/messages.py +416 -0
  38. linq/resources/chats/participants.py +322 -0
  39. linq/resources/chats/typing.py +299 -0
  40. linq/resources/contact_card.py +472 -0
  41. linq/resources/messages.py +686 -0
  42. linq/resources/phone_numbers.py +163 -0
  43. linq/resources/phonenumbers.py +165 -0
  44. linq/resources/webhook_events.py +319 -0
  45. linq/resources/webhook_subscriptions.py +776 -0
  46. linq/resources/webhooks.py +34 -0
  47. linq/types/__init__.py +90 -0
  48. linq/types/attachment_create_params.py +42 -0
  49. linq/types/attachment_create_response.py +44 -0
  50. linq/types/attachment_retrieve_response.py +55 -0
  51. linq/types/capability_check_RCS_params.py +20 -0
  52. linq/types/capability_check_i_message_params.py +20 -0
  53. linq/types/chat.py +44 -0
  54. linq/types/chat_create_params.py +33 -0
  55. linq/types/chat_create_response.py +44 -0
  56. linq/types/chat_created_webhook_event.py +87 -0
  57. linq/types/chat_group_icon_update_failed_webhook_event.py +65 -0
  58. linq/types/chat_group_icon_updated_webhook_event.py +66 -0
  59. linq/types/chat_group_name_update_failed_webhook_event.py +65 -0
  60. linq/types/chat_group_name_updated_webhook_event.py +66 -0
  61. linq/types/chat_leave_chat_response.py +15 -0
  62. linq/types/chat_list_chats_params.py +36 -0
  63. linq/types/chat_send_voicememo_params.py +23 -0
  64. linq/types/chat_send_voicememo_response.py +79 -0
  65. linq/types/chat_typing_indicator_started_webhook_event.py +52 -0
  66. linq/types/chat_typing_indicator_stopped_webhook_event.py +52 -0
  67. linq/types/chat_update_params.py +15 -0
  68. linq/types/chat_update_response.py +13 -0
  69. linq/types/chats/__init__.py +12 -0
  70. linq/types/chats/message_list_params.py +15 -0
  71. linq/types/chats/message_send_params.py +18 -0
  72. linq/types/chats/message_send_response.py +16 -0
  73. linq/types/chats/participant_add_params.py +12 -0
  74. linq/types/chats/participant_add_response.py +15 -0
  75. linq/types/chats/participant_remove_params.py +12 -0
  76. linq/types/chats/participant_remove_response.py +15 -0
  77. linq/types/chats/sent_message.py +69 -0
  78. linq/types/contact_card_create_params.py +24 -0
  79. linq/types/contact_card_retrieve_params.py +15 -0
  80. linq/types/contact_card_retrieve_response.py +23 -0
  81. linq/types/contact_card_update_params.py +21 -0
  82. linq/types/events_webhook_event.py +50 -0
  83. linq/types/handle_check_response.py +13 -0
  84. linq/types/link_part_param.py +22 -0
  85. linq/types/media_part_param.py +54 -0
  86. linq/types/message.py +87 -0
  87. linq/types/message_add_reaction_params.py +32 -0
  88. linq/types/message_add_reaction_response.py +15 -0
  89. linq/types/message_content_param.py +82 -0
  90. linq/types/message_delivered_webhook_event.py +65 -0
  91. linq/types/message_edited_webhook_event.py +100 -0
  92. linq/types/message_effect.py +23 -0
  93. linq/types/message_effect_param.py +22 -0
  94. linq/types/message_event_v2.py +116 -0
  95. linq/types/message_failed_webhook_event.py +72 -0
  96. linq/types/message_list_messages_thread_params.py +18 -0
  97. linq/types/message_read_webhook_event.py +65 -0
  98. linq/types/message_received_webhook_event.py +65 -0
  99. linq/types/message_sent_webhook_event.py +65 -0
  100. linq/types/message_update_params.py +15 -0
  101. linq/types/participant_added_webhook_event.py +66 -0
  102. linq/types/participant_removed_webhook_event.py +66 -0
  103. linq/types/phone_number_list_response.py +20 -0
  104. linq/types/phone_number_status_updated_webhook_event.py +82 -0
  105. linq/types/phonenumber_list_response.py +39 -0
  106. linq/types/reaction_added_webhook_event.py +46 -0
  107. linq/types/reaction_event_base.py +85 -0
  108. linq/types/reaction_removed_webhook_event.py +46 -0
  109. linq/types/reply_to.py +21 -0
  110. linq/types/reply_to_param.py +21 -0
  111. linq/types/schemas_media_part_response.py +29 -0
  112. linq/types/schemas_message_effect.py +18 -0
  113. linq/types/schemas_text_part_response.py +22 -0
  114. linq/types/set_contact_card.py +24 -0
  115. linq/types/shared/__init__.py +9 -0
  116. linq/types/shared/chat_handle.py +33 -0
  117. linq/types/shared/media_part_response.py +34 -0
  118. linq/types/shared/reaction.py +56 -0
  119. linq/types/shared/reaction_type.py +7 -0
  120. linq/types/shared/service_type.py +7 -0
  121. linq/types/shared/text_decoration.py +23 -0
  122. linq/types/shared/text_part_response.py +26 -0
  123. linq/types/shared_params/__init__.py +5 -0
  124. linq/types/shared_params/reaction_type.py +9 -0
  125. linq/types/shared_params/service_type.py +9 -0
  126. linq/types/shared_params/text_decoration.py +23 -0
  127. linq/types/supported_content_type.py +60 -0
  128. linq/types/text_part_param.py +44 -0
  129. linq/types/webhook_event_list_response.py +17 -0
  130. linq/types/webhook_event_type.py +33 -0
  131. linq/types/webhook_subscription.py +35 -0
  132. linq/types/webhook_subscription_create_params.py +27 -0
  133. linq/types/webhook_subscription_create_response.py +46 -0
  134. linq/types/webhook_subscription_list_response.py +13 -0
  135. linq/types/webhook_subscription_update_params.py +30 -0
  136. linq_python-0.1.0.dist-info/METADATA +572 -0
  137. linq_python-0.1.0.dist-info/RECORD +139 -0
  138. linq_python-0.1.0.dist-info/WHEEL +4 -0
  139. linq_python-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,163 @@
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ import httpx
6
+
7
+ from .._types import Body, Query, Headers, NotGiven, not_given
8
+ from .._compat import cached_property
9
+ from .._resource import SyncAPIResource, AsyncAPIResource
10
+ from .._response import (
11
+ to_raw_response_wrapper,
12
+ to_streamed_response_wrapper,
13
+ async_to_raw_response_wrapper,
14
+ async_to_streamed_response_wrapper,
15
+ )
16
+ from .._base_client import make_request_options
17
+ from ..types.phone_number_list_response import PhoneNumberListResponse
18
+
19
+ __all__ = ["PhoneNumbersResource", "AsyncPhoneNumbersResource"]
20
+
21
+
22
+ class PhoneNumbersResource(SyncAPIResource):
23
+ """Phone Numbers represent the phone numbers assigned to your partner account.
24
+
25
+ Use the list phone numbers endpoint to discover which phone numbers are available
26
+ for sending messages.
27
+
28
+ When creating chats, listing chats, or sending a voice memo, use one of your assigned phone numbers
29
+ in the `from` field.
30
+ """
31
+
32
+ @cached_property
33
+ def with_raw_response(self) -> PhoneNumbersResourceWithRawResponse:
34
+ """
35
+ This property can be used as a prefix for any HTTP method call to return
36
+ the raw response object instead of the parsed content.
37
+
38
+ For more information, see https://www.github.com/linq-team/linq-python#accessing-raw-response-data-eg-headers
39
+ """
40
+ return PhoneNumbersResourceWithRawResponse(self)
41
+
42
+ @cached_property
43
+ def with_streaming_response(self) -> PhoneNumbersResourceWithStreamingResponse:
44
+ """
45
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
46
+
47
+ For more information, see https://www.github.com/linq-team/linq-python#with_streaming_response
48
+ """
49
+ return PhoneNumbersResourceWithStreamingResponse(self)
50
+
51
+ def list(
52
+ self,
53
+ *,
54
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
55
+ # The extra values given here take precedence over values defined on the client or passed to this method.
56
+ extra_headers: Headers | None = None,
57
+ extra_query: Query | None = None,
58
+ extra_body: Body | None = None,
59
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
60
+ ) -> PhoneNumberListResponse:
61
+ """Returns all phone numbers assigned to the authenticated partner.
62
+
63
+ Use this
64
+ endpoint to discover which phone numbers are available for use as the `from`
65
+ field when creating a chat, listing chats, or sending a voice memo.
66
+ """
67
+ return self._get(
68
+ "/v3/phone_numbers",
69
+ options=make_request_options(
70
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
71
+ ),
72
+ cast_to=PhoneNumberListResponse,
73
+ )
74
+
75
+
76
+ class AsyncPhoneNumbersResource(AsyncAPIResource):
77
+ """Phone Numbers represent the phone numbers assigned to your partner account.
78
+
79
+ Use the list phone numbers endpoint to discover which phone numbers are available
80
+ for sending messages.
81
+
82
+ When creating chats, listing chats, or sending a voice memo, use one of your assigned phone numbers
83
+ in the `from` field.
84
+ """
85
+
86
+ @cached_property
87
+ def with_raw_response(self) -> AsyncPhoneNumbersResourceWithRawResponse:
88
+ """
89
+ This property can be used as a prefix for any HTTP method call to return
90
+ the raw response object instead of the parsed content.
91
+
92
+ For more information, see https://www.github.com/linq-team/linq-python#accessing-raw-response-data-eg-headers
93
+ """
94
+ return AsyncPhoneNumbersResourceWithRawResponse(self)
95
+
96
+ @cached_property
97
+ def with_streaming_response(self) -> AsyncPhoneNumbersResourceWithStreamingResponse:
98
+ """
99
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
100
+
101
+ For more information, see https://www.github.com/linq-team/linq-python#with_streaming_response
102
+ """
103
+ return AsyncPhoneNumbersResourceWithStreamingResponse(self)
104
+
105
+ async def list(
106
+ self,
107
+ *,
108
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
109
+ # The extra values given here take precedence over values defined on the client or passed to this method.
110
+ extra_headers: Headers | None = None,
111
+ extra_query: Query | None = None,
112
+ extra_body: Body | None = None,
113
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
114
+ ) -> PhoneNumberListResponse:
115
+ """Returns all phone numbers assigned to the authenticated partner.
116
+
117
+ Use this
118
+ endpoint to discover which phone numbers are available for use as the `from`
119
+ field when creating a chat, listing chats, or sending a voice memo.
120
+ """
121
+ return await self._get(
122
+ "/v3/phone_numbers",
123
+ options=make_request_options(
124
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
125
+ ),
126
+ cast_to=PhoneNumberListResponse,
127
+ )
128
+
129
+
130
+ class PhoneNumbersResourceWithRawResponse:
131
+ def __init__(self, phone_numbers: PhoneNumbersResource) -> None:
132
+ self._phone_numbers = phone_numbers
133
+
134
+ self.list = to_raw_response_wrapper(
135
+ phone_numbers.list,
136
+ )
137
+
138
+
139
+ class AsyncPhoneNumbersResourceWithRawResponse:
140
+ def __init__(self, phone_numbers: AsyncPhoneNumbersResource) -> None:
141
+ self._phone_numbers = phone_numbers
142
+
143
+ self.list = async_to_raw_response_wrapper(
144
+ phone_numbers.list,
145
+ )
146
+
147
+
148
+ class PhoneNumbersResourceWithStreamingResponse:
149
+ def __init__(self, phone_numbers: PhoneNumbersResource) -> None:
150
+ self._phone_numbers = phone_numbers
151
+
152
+ self.list = to_streamed_response_wrapper(
153
+ phone_numbers.list,
154
+ )
155
+
156
+
157
+ class AsyncPhoneNumbersResourceWithStreamingResponse:
158
+ def __init__(self, phone_numbers: AsyncPhoneNumbersResource) -> None:
159
+ self._phone_numbers = phone_numbers
160
+
161
+ self.list = async_to_streamed_response_wrapper(
162
+ phone_numbers.list,
163
+ )
@@ -0,0 +1,165 @@
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ import typing_extensions
6
+
7
+ import httpx
8
+
9
+ from .._types import Body, Query, Headers, NotGiven, not_given
10
+ from .._compat import cached_property
11
+ from .._resource import SyncAPIResource, AsyncAPIResource
12
+ from .._response import (
13
+ to_raw_response_wrapper,
14
+ to_streamed_response_wrapper,
15
+ async_to_raw_response_wrapper,
16
+ async_to_streamed_response_wrapper,
17
+ )
18
+ from .._base_client import make_request_options
19
+ from ..types.phonenumber_list_response import PhonenumberListResponse
20
+
21
+ __all__ = ["PhonenumbersResource", "AsyncPhonenumbersResource"]
22
+
23
+
24
+ class PhonenumbersResource(SyncAPIResource):
25
+ """Phone Numbers represent the phone numbers assigned to your partner account.
26
+
27
+ Use the list phone numbers endpoint to discover which phone numbers are available
28
+ for sending messages.
29
+
30
+ When creating chats, listing chats, or sending a voice memo, use one of your assigned phone numbers
31
+ in the `from` field.
32
+ """
33
+
34
+ @cached_property
35
+ def with_raw_response(self) -> PhonenumbersResourceWithRawResponse:
36
+ """
37
+ This property can be used as a prefix for any HTTP method call to return
38
+ the raw response object instead of the parsed content.
39
+
40
+ For more information, see https://www.github.com/linq-team/linq-python#accessing-raw-response-data-eg-headers
41
+ """
42
+ return PhonenumbersResourceWithRawResponse(self)
43
+
44
+ @cached_property
45
+ def with_streaming_response(self) -> PhonenumbersResourceWithStreamingResponse:
46
+ """
47
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
48
+
49
+ For more information, see https://www.github.com/linq-team/linq-python#with_streaming_response
50
+ """
51
+ return PhonenumbersResourceWithStreamingResponse(self)
52
+
53
+ @typing_extensions.deprecated("deprecated")
54
+ def list(
55
+ self,
56
+ *,
57
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
58
+ # The extra values given here take precedence over values defined on the client or passed to this method.
59
+ extra_headers: Headers | None = None,
60
+ extra_query: Query | None = None,
61
+ extra_body: Body | None = None,
62
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
63
+ ) -> PhonenumberListResponse:
64
+ """**Deprecated.** Use `GET /v3/phone_numbers` instead."""
65
+ return self._get(
66
+ "/v3/phonenumbers",
67
+ options=make_request_options(
68
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
69
+ ),
70
+ cast_to=PhonenumberListResponse,
71
+ )
72
+
73
+
74
+ class AsyncPhonenumbersResource(AsyncAPIResource):
75
+ """Phone Numbers represent the phone numbers assigned to your partner account.
76
+
77
+ Use the list phone numbers endpoint to discover which phone numbers are available
78
+ for sending messages.
79
+
80
+ When creating chats, listing chats, or sending a voice memo, use one of your assigned phone numbers
81
+ in the `from` field.
82
+ """
83
+
84
+ @cached_property
85
+ def with_raw_response(self) -> AsyncPhonenumbersResourceWithRawResponse:
86
+ """
87
+ This property can be used as a prefix for any HTTP method call to return
88
+ the raw response object instead of the parsed content.
89
+
90
+ For more information, see https://www.github.com/linq-team/linq-python#accessing-raw-response-data-eg-headers
91
+ """
92
+ return AsyncPhonenumbersResourceWithRawResponse(self)
93
+
94
+ @cached_property
95
+ def with_streaming_response(self) -> AsyncPhonenumbersResourceWithStreamingResponse:
96
+ """
97
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
98
+
99
+ For more information, see https://www.github.com/linq-team/linq-python#with_streaming_response
100
+ """
101
+ return AsyncPhonenumbersResourceWithStreamingResponse(self)
102
+
103
+ @typing_extensions.deprecated("deprecated")
104
+ async def list(
105
+ self,
106
+ *,
107
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
108
+ # The extra values given here take precedence over values defined on the client or passed to this method.
109
+ extra_headers: Headers | None = None,
110
+ extra_query: Query | None = None,
111
+ extra_body: Body | None = None,
112
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
113
+ ) -> PhonenumberListResponse:
114
+ """**Deprecated.** Use `GET /v3/phone_numbers` instead."""
115
+ return await self._get(
116
+ "/v3/phonenumbers",
117
+ options=make_request_options(
118
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
119
+ ),
120
+ cast_to=PhonenumberListResponse,
121
+ )
122
+
123
+
124
+ class PhonenumbersResourceWithRawResponse:
125
+ def __init__(self, phonenumbers: PhonenumbersResource) -> None:
126
+ self._phonenumbers = phonenumbers
127
+
128
+ self.list = ( # pyright: ignore[reportDeprecated]
129
+ to_raw_response_wrapper(
130
+ phonenumbers.list, # pyright: ignore[reportDeprecated],
131
+ )
132
+ )
133
+
134
+
135
+ class AsyncPhonenumbersResourceWithRawResponse:
136
+ def __init__(self, phonenumbers: AsyncPhonenumbersResource) -> None:
137
+ self._phonenumbers = phonenumbers
138
+
139
+ self.list = ( # pyright: ignore[reportDeprecated]
140
+ async_to_raw_response_wrapper(
141
+ phonenumbers.list, # pyright: ignore[reportDeprecated],
142
+ )
143
+ )
144
+
145
+
146
+ class PhonenumbersResourceWithStreamingResponse:
147
+ def __init__(self, phonenumbers: PhonenumbersResource) -> None:
148
+ self._phonenumbers = phonenumbers
149
+
150
+ self.list = ( # pyright: ignore[reportDeprecated]
151
+ to_streamed_response_wrapper(
152
+ phonenumbers.list, # pyright: ignore[reportDeprecated],
153
+ )
154
+ )
155
+
156
+
157
+ class AsyncPhonenumbersResourceWithStreamingResponse:
158
+ def __init__(self, phonenumbers: AsyncPhonenumbersResource) -> None:
159
+ self._phonenumbers = phonenumbers
160
+
161
+ self.list = ( # pyright: ignore[reportDeprecated]
162
+ async_to_streamed_response_wrapper(
163
+ phonenumbers.list, # pyright: ignore[reportDeprecated],
164
+ )
165
+ )
@@ -0,0 +1,319 @@
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ import httpx
6
+
7
+ from .._types import Body, Query, Headers, NotGiven, not_given
8
+ from .._compat import cached_property
9
+ from .._resource import SyncAPIResource, AsyncAPIResource
10
+ from .._response import (
11
+ to_raw_response_wrapper,
12
+ to_streamed_response_wrapper,
13
+ async_to_raw_response_wrapper,
14
+ async_to_streamed_response_wrapper,
15
+ )
16
+ from .._base_client import make_request_options
17
+ from ..types.webhook_event_list_response import WebhookEventListResponse
18
+
19
+ __all__ = ["WebhookEventsResource", "AsyncWebhookEventsResource"]
20
+
21
+
22
+ class WebhookEventsResource(SyncAPIResource):
23
+ """
24
+ Webhook Subscriptions allow you to receive real-time notifications when events
25
+ occur on your account.
26
+
27
+ Configure webhook endpoints to receive events such as messages sent/received,
28
+ delivery status changes, reactions, typing indicators, and more.
29
+
30
+ Failed deliveries (5xx, 429, network errors) are retried up to 10 times over
31
+ ~25 minutes with exponential backoff. Each event includes a unique ID for
32
+ deduplication.
33
+
34
+ ## Webhook Headers
35
+
36
+ Each webhook request includes the following headers:
37
+
38
+ | Header | Description |
39
+ |--------|-------------|
40
+ | `X-Webhook-Event` | The event type (e.g., `message.sent`, `message.received`) |
41
+ | `X-Webhook-Subscription-ID` | Your webhook subscription ID |
42
+ | `X-Webhook-Timestamp` | Unix timestamp (seconds) when the webhook was sent |
43
+ | `X-Webhook-Signature` | HMAC-SHA256 signature for verification |
44
+
45
+ ## Verifying Webhook Signatures
46
+
47
+ All webhooks are signed using HMAC-SHA256. You should always verify the signature
48
+ to ensure the webhook originated from Linq and hasn't been tampered with.
49
+
50
+ **Signature Construction:**
51
+
52
+ The signature is computed over a concatenation of the timestamp and payload:
53
+
54
+ ```
55
+ {timestamp}.{payload}
56
+ ```
57
+
58
+ Where:
59
+ - `timestamp` is the value from the `X-Webhook-Timestamp` header
60
+ - `payload` is the raw JSON request body (exact bytes, not re-serialized)
61
+
62
+ **Verification Steps:**
63
+
64
+ 1. Extract the `X-Webhook-Timestamp` and `X-Webhook-Signature` headers
65
+ 2. Get the raw request body bytes (do not parse and re-serialize)
66
+ 3. Concatenate: `"{timestamp}.{payload}"`
67
+ 4. Compute HMAC-SHA256 using your signing secret as the key
68
+ 5. Hex-encode the result and compare with `X-Webhook-Signature`
69
+ 6. Use constant-time comparison to prevent timing attacks
70
+
71
+ **Example (Python):**
72
+
73
+ ```python
74
+ import hmac
75
+ import hashlib
76
+
77
+
78
+ def verify_webhook(signing_secret, payload, timestamp, signature):
79
+ message = f"{timestamp}.{payload.decode('utf-8')}"
80
+ expected = hmac.new(signing_secret.encode("utf-8"), message.encode("utf-8"), hashlib.sha256).hexdigest()
81
+ return hmac.compare_digest(expected, signature)
82
+ ```
83
+
84
+ **Example (Node.js):**
85
+
86
+ ```javascript
87
+ const crypto = require('crypto');
88
+
89
+ function verifyWebhook(signingSecret, payload, timestamp, signature) {
90
+ const message = `${timestamp}.${payload}`;
91
+ const expected = crypto
92
+ .createHmac('sha256', signingSecret)
93
+ .update(message)
94
+ .digest('hex');
95
+ return crypto.timingSafeEqual(
96
+ Buffer.from(expected),
97
+ Buffer.from(signature)
98
+ );
99
+ }
100
+ ```
101
+
102
+ **Security Best Practices:**
103
+
104
+ - Reject webhooks with timestamps older than 5 minutes to prevent replay attacks
105
+ - Always use constant-time comparison for signature verification
106
+ - Store your signing secret securely (e.g., environment variable, secrets manager)
107
+ - Return a 2xx status code quickly, then process the webhook asynchronously
108
+ """
109
+
110
+ @cached_property
111
+ def with_raw_response(self) -> WebhookEventsResourceWithRawResponse:
112
+ """
113
+ This property can be used as a prefix for any HTTP method call to return
114
+ the raw response object instead of the parsed content.
115
+
116
+ For more information, see https://www.github.com/linq-team/linq-python#accessing-raw-response-data-eg-headers
117
+ """
118
+ return WebhookEventsResourceWithRawResponse(self)
119
+
120
+ @cached_property
121
+ def with_streaming_response(self) -> WebhookEventsResourceWithStreamingResponse:
122
+ """
123
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
124
+
125
+ For more information, see https://www.github.com/linq-team/linq-python#with_streaming_response
126
+ """
127
+ return WebhookEventsResourceWithStreamingResponse(self)
128
+
129
+ def list(
130
+ self,
131
+ *,
132
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
133
+ # The extra values given here take precedence over values defined on the client or passed to this method.
134
+ extra_headers: Headers | None = None,
135
+ extra_query: Query | None = None,
136
+ extra_body: Body | None = None,
137
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
138
+ ) -> WebhookEventListResponse:
139
+ """Returns all available webhook event types that can be subscribed to.
140
+
141
+ Use this
142
+ endpoint to discover valid values for the `subscribed_events` field when
143
+ creating or updating webhook subscriptions.
144
+ """
145
+ return self._get(
146
+ "/v3/webhook-events",
147
+ options=make_request_options(
148
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
149
+ ),
150
+ cast_to=WebhookEventListResponse,
151
+ )
152
+
153
+
154
+ class AsyncWebhookEventsResource(AsyncAPIResource):
155
+ """
156
+ Webhook Subscriptions allow you to receive real-time notifications when events
157
+ occur on your account.
158
+
159
+ Configure webhook endpoints to receive events such as messages sent/received,
160
+ delivery status changes, reactions, typing indicators, and more.
161
+
162
+ Failed deliveries (5xx, 429, network errors) are retried up to 10 times over
163
+ ~25 minutes with exponential backoff. Each event includes a unique ID for
164
+ deduplication.
165
+
166
+ ## Webhook Headers
167
+
168
+ Each webhook request includes the following headers:
169
+
170
+ | Header | Description |
171
+ |--------|-------------|
172
+ | `X-Webhook-Event` | The event type (e.g., `message.sent`, `message.received`) |
173
+ | `X-Webhook-Subscription-ID` | Your webhook subscription ID |
174
+ | `X-Webhook-Timestamp` | Unix timestamp (seconds) when the webhook was sent |
175
+ | `X-Webhook-Signature` | HMAC-SHA256 signature for verification |
176
+
177
+ ## Verifying Webhook Signatures
178
+
179
+ All webhooks are signed using HMAC-SHA256. You should always verify the signature
180
+ to ensure the webhook originated from Linq and hasn't been tampered with.
181
+
182
+ **Signature Construction:**
183
+
184
+ The signature is computed over a concatenation of the timestamp and payload:
185
+
186
+ ```
187
+ {timestamp}.{payload}
188
+ ```
189
+
190
+ Where:
191
+ - `timestamp` is the value from the `X-Webhook-Timestamp` header
192
+ - `payload` is the raw JSON request body (exact bytes, not re-serialized)
193
+
194
+ **Verification Steps:**
195
+
196
+ 1. Extract the `X-Webhook-Timestamp` and `X-Webhook-Signature` headers
197
+ 2. Get the raw request body bytes (do not parse and re-serialize)
198
+ 3. Concatenate: `"{timestamp}.{payload}"`
199
+ 4. Compute HMAC-SHA256 using your signing secret as the key
200
+ 5. Hex-encode the result and compare with `X-Webhook-Signature`
201
+ 6. Use constant-time comparison to prevent timing attacks
202
+
203
+ **Example (Python):**
204
+
205
+ ```python
206
+ import hmac
207
+ import hashlib
208
+
209
+
210
+ def verify_webhook(signing_secret, payload, timestamp, signature):
211
+ message = f"{timestamp}.{payload.decode('utf-8')}"
212
+ expected = hmac.new(signing_secret.encode("utf-8"), message.encode("utf-8"), hashlib.sha256).hexdigest()
213
+ return hmac.compare_digest(expected, signature)
214
+ ```
215
+
216
+ **Example (Node.js):**
217
+
218
+ ```javascript
219
+ const crypto = require('crypto');
220
+
221
+ function verifyWebhook(signingSecret, payload, timestamp, signature) {
222
+ const message = `${timestamp}.${payload}`;
223
+ const expected = crypto
224
+ .createHmac('sha256', signingSecret)
225
+ .update(message)
226
+ .digest('hex');
227
+ return crypto.timingSafeEqual(
228
+ Buffer.from(expected),
229
+ Buffer.from(signature)
230
+ );
231
+ }
232
+ ```
233
+
234
+ **Security Best Practices:**
235
+
236
+ - Reject webhooks with timestamps older than 5 minutes to prevent replay attacks
237
+ - Always use constant-time comparison for signature verification
238
+ - Store your signing secret securely (e.g., environment variable, secrets manager)
239
+ - Return a 2xx status code quickly, then process the webhook asynchronously
240
+ """
241
+
242
+ @cached_property
243
+ def with_raw_response(self) -> AsyncWebhookEventsResourceWithRawResponse:
244
+ """
245
+ This property can be used as a prefix for any HTTP method call to return
246
+ the raw response object instead of the parsed content.
247
+
248
+ For more information, see https://www.github.com/linq-team/linq-python#accessing-raw-response-data-eg-headers
249
+ """
250
+ return AsyncWebhookEventsResourceWithRawResponse(self)
251
+
252
+ @cached_property
253
+ def with_streaming_response(self) -> AsyncWebhookEventsResourceWithStreamingResponse:
254
+ """
255
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
256
+
257
+ For more information, see https://www.github.com/linq-team/linq-python#with_streaming_response
258
+ """
259
+ return AsyncWebhookEventsResourceWithStreamingResponse(self)
260
+
261
+ async def list(
262
+ self,
263
+ *,
264
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
265
+ # The extra values given here take precedence over values defined on the client or passed to this method.
266
+ extra_headers: Headers | None = None,
267
+ extra_query: Query | None = None,
268
+ extra_body: Body | None = None,
269
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
270
+ ) -> WebhookEventListResponse:
271
+ """Returns all available webhook event types that can be subscribed to.
272
+
273
+ Use this
274
+ endpoint to discover valid values for the `subscribed_events` field when
275
+ creating or updating webhook subscriptions.
276
+ """
277
+ return await self._get(
278
+ "/v3/webhook-events",
279
+ options=make_request_options(
280
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
281
+ ),
282
+ cast_to=WebhookEventListResponse,
283
+ )
284
+
285
+
286
+ class WebhookEventsResourceWithRawResponse:
287
+ def __init__(self, webhook_events: WebhookEventsResource) -> None:
288
+ self._webhook_events = webhook_events
289
+
290
+ self.list = to_raw_response_wrapper(
291
+ webhook_events.list,
292
+ )
293
+
294
+
295
+ class AsyncWebhookEventsResourceWithRawResponse:
296
+ def __init__(self, webhook_events: AsyncWebhookEventsResource) -> None:
297
+ self._webhook_events = webhook_events
298
+
299
+ self.list = async_to_raw_response_wrapper(
300
+ webhook_events.list,
301
+ )
302
+
303
+
304
+ class WebhookEventsResourceWithStreamingResponse:
305
+ def __init__(self, webhook_events: WebhookEventsResource) -> None:
306
+ self._webhook_events = webhook_events
307
+
308
+ self.list = to_streamed_response_wrapper(
309
+ webhook_events.list,
310
+ )
311
+
312
+
313
+ class AsyncWebhookEventsResourceWithStreamingResponse:
314
+ def __init__(self, webhook_events: AsyncWebhookEventsResource) -> None:
315
+ self._webhook_events = webhook_events
316
+
317
+ self.list = async_to_streamed_response_wrapper(
318
+ webhook_events.list,
319
+ )