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.
- linq/__init__.py +102 -0
- linq/_base_client.py +2149 -0
- linq/_client.py +2479 -0
- linq/_compat.py +226 -0
- linq/_constants.py +14 -0
- linq/_exceptions.py +108 -0
- linq/_files.py +123 -0
- linq/_models.py +878 -0
- linq/_qs.py +153 -0
- linq/_resource.py +43 -0
- linq/_response.py +833 -0
- linq/_streaming.py +338 -0
- linq/_types.py +271 -0
- linq/_utils/__init__.py +65 -0
- linq/_utils/_compat.py +45 -0
- linq/_utils/_datetime_parse.py +136 -0
- linq/_utils/_json.py +35 -0
- linq/_utils/_logs.py +25 -0
- linq/_utils/_path.py +127 -0
- linq/_utils/_proxy.py +65 -0
- linq/_utils/_reflection.py +42 -0
- linq/_utils/_resources_proxy.py +24 -0
- linq/_utils/_streams.py +12 -0
- linq/_utils/_sync.py +58 -0
- linq/_utils/_transform.py +457 -0
- linq/_utils/_typing.py +156 -0
- linq/_utils/_utils.py +421 -0
- linq/_version.py +4 -0
- linq/lib/.keep +4 -0
- linq/pagination.py +95 -0
- linq/py.typed +0 -0
- linq/resources/__init__.py +134 -0
- linq/resources/attachments.py +589 -0
- linq/resources/capability.py +297 -0
- linq/resources/chats/__init__.py +61 -0
- linq/resources/chats/chats.py +1492 -0
- linq/resources/chats/messages.py +416 -0
- linq/resources/chats/participants.py +322 -0
- linq/resources/chats/typing.py +299 -0
- linq/resources/contact_card.py +472 -0
- linq/resources/messages.py +686 -0
- linq/resources/phone_numbers.py +163 -0
- linq/resources/phonenumbers.py +165 -0
- linq/resources/webhook_events.py +319 -0
- linq/resources/webhook_subscriptions.py +776 -0
- linq/resources/webhooks.py +34 -0
- linq/types/__init__.py +90 -0
- linq/types/attachment_create_params.py +42 -0
- linq/types/attachment_create_response.py +44 -0
- linq/types/attachment_retrieve_response.py +55 -0
- linq/types/capability_check_RCS_params.py +20 -0
- linq/types/capability_check_i_message_params.py +20 -0
- linq/types/chat.py +44 -0
- linq/types/chat_create_params.py +33 -0
- linq/types/chat_create_response.py +44 -0
- linq/types/chat_created_webhook_event.py +87 -0
- linq/types/chat_group_icon_update_failed_webhook_event.py +65 -0
- linq/types/chat_group_icon_updated_webhook_event.py +66 -0
- linq/types/chat_group_name_update_failed_webhook_event.py +65 -0
- linq/types/chat_group_name_updated_webhook_event.py +66 -0
- linq/types/chat_leave_chat_response.py +15 -0
- linq/types/chat_list_chats_params.py +36 -0
- linq/types/chat_send_voicememo_params.py +23 -0
- linq/types/chat_send_voicememo_response.py +79 -0
- linq/types/chat_typing_indicator_started_webhook_event.py +52 -0
- linq/types/chat_typing_indicator_stopped_webhook_event.py +52 -0
- linq/types/chat_update_params.py +15 -0
- linq/types/chat_update_response.py +13 -0
- linq/types/chats/__init__.py +12 -0
- linq/types/chats/message_list_params.py +15 -0
- linq/types/chats/message_send_params.py +18 -0
- linq/types/chats/message_send_response.py +16 -0
- linq/types/chats/participant_add_params.py +12 -0
- linq/types/chats/participant_add_response.py +15 -0
- linq/types/chats/participant_remove_params.py +12 -0
- linq/types/chats/participant_remove_response.py +15 -0
- linq/types/chats/sent_message.py +69 -0
- linq/types/contact_card_create_params.py +24 -0
- linq/types/contact_card_retrieve_params.py +15 -0
- linq/types/contact_card_retrieve_response.py +23 -0
- linq/types/contact_card_update_params.py +21 -0
- linq/types/events_webhook_event.py +50 -0
- linq/types/handle_check_response.py +13 -0
- linq/types/link_part_param.py +22 -0
- linq/types/media_part_param.py +54 -0
- linq/types/message.py +87 -0
- linq/types/message_add_reaction_params.py +32 -0
- linq/types/message_add_reaction_response.py +15 -0
- linq/types/message_content_param.py +82 -0
- linq/types/message_delivered_webhook_event.py +65 -0
- linq/types/message_edited_webhook_event.py +100 -0
- linq/types/message_effect.py +23 -0
- linq/types/message_effect_param.py +22 -0
- linq/types/message_event_v2.py +116 -0
- linq/types/message_failed_webhook_event.py +72 -0
- linq/types/message_list_messages_thread_params.py +18 -0
- linq/types/message_read_webhook_event.py +65 -0
- linq/types/message_received_webhook_event.py +65 -0
- linq/types/message_sent_webhook_event.py +65 -0
- linq/types/message_update_params.py +15 -0
- linq/types/participant_added_webhook_event.py +66 -0
- linq/types/participant_removed_webhook_event.py +66 -0
- linq/types/phone_number_list_response.py +20 -0
- linq/types/phone_number_status_updated_webhook_event.py +82 -0
- linq/types/phonenumber_list_response.py +39 -0
- linq/types/reaction_added_webhook_event.py +46 -0
- linq/types/reaction_event_base.py +85 -0
- linq/types/reaction_removed_webhook_event.py +46 -0
- linq/types/reply_to.py +21 -0
- linq/types/reply_to_param.py +21 -0
- linq/types/schemas_media_part_response.py +29 -0
- linq/types/schemas_message_effect.py +18 -0
- linq/types/schemas_text_part_response.py +22 -0
- linq/types/set_contact_card.py +24 -0
- linq/types/shared/__init__.py +9 -0
- linq/types/shared/chat_handle.py +33 -0
- linq/types/shared/media_part_response.py +34 -0
- linq/types/shared/reaction.py +56 -0
- linq/types/shared/reaction_type.py +7 -0
- linq/types/shared/service_type.py +7 -0
- linq/types/shared/text_decoration.py +23 -0
- linq/types/shared/text_part_response.py +26 -0
- linq/types/shared_params/__init__.py +5 -0
- linq/types/shared_params/reaction_type.py +9 -0
- linq/types/shared_params/service_type.py +9 -0
- linq/types/shared_params/text_decoration.py +23 -0
- linq/types/supported_content_type.py +60 -0
- linq/types/text_part_param.py +44 -0
- linq/types/webhook_event_list_response.py +17 -0
- linq/types/webhook_event_type.py +33 -0
- linq/types/webhook_subscription.py +35 -0
- linq/types/webhook_subscription_create_params.py +27 -0
- linq/types/webhook_subscription_create_response.py +46 -0
- linq/types/webhook_subscription_list_response.py +13 -0
- linq/types/webhook_subscription_update_params.py +30 -0
- linq_python-0.1.0.dist-info/METADATA +572 -0
- linq_python-0.1.0.dist-info/RECORD +139 -0
- linq_python-0.1.0.dist-info/WHEEL +4 -0
- linq_python-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,589 @@
|
|
|
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 SupportedContentType, attachment_create_params
|
|
8
|
+
from .._types import Body, Query, Headers, NotGiven, not_given
|
|
9
|
+
from .._utils import path_template, maybe_transform, async_maybe_transform
|
|
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.supported_content_type import SupportedContentType
|
|
20
|
+
from ..types.attachment_create_response import AttachmentCreateResponse
|
|
21
|
+
from ..types.attachment_retrieve_response import AttachmentRetrieveResponse
|
|
22
|
+
|
|
23
|
+
__all__ = ["AttachmentsResource", "AsyncAttachmentsResource"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AttachmentsResource(SyncAPIResource):
|
|
27
|
+
"""
|
|
28
|
+
Send files (images, videos, documents, audio) with messages by providing a URL in a media part.
|
|
29
|
+
Pre-uploading via `POST /v3/attachments` is **optional** and only needed for specific optimization scenarios.
|
|
30
|
+
|
|
31
|
+
## Sending Media via URL (up to 10MB)
|
|
32
|
+
|
|
33
|
+
Provide a publicly accessible HTTPS URL with a [supported media type](#supported-file-types) in the `url` field of a media part.
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"parts": [
|
|
38
|
+
{ "type": "media", "url": "https://your-cdn.com/images/photo.jpg" }
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This works with any URL you already host — no pre-upload step required. **Maximum file size: 10MB.**
|
|
44
|
+
|
|
45
|
+
## Pre-Upload (required for files over 10MB)
|
|
46
|
+
|
|
47
|
+
Use `POST /v3/attachments` when you want to:
|
|
48
|
+
- **Send files larger than 10MB** (up to 100MB) — URL-based downloads are limited to 10MB
|
|
49
|
+
- **Send the same file to many recipients** — upload once, reuse the `attachment_id` without re-downloading each time
|
|
50
|
+
- **Reduce message send latency** — the file is already stored, so sending is faster
|
|
51
|
+
|
|
52
|
+
**How it works:**
|
|
53
|
+
1. `POST /v3/attachments` with file metadata → returns a presigned `upload_url` (valid for **15 minutes**) and a permanent `attachment_id`
|
|
54
|
+
2. PUT the raw file bytes to the `upload_url` with the `required_headers` (no JSON or multipart — just the binary content)
|
|
55
|
+
3. Reference the `attachment_id` in your media part when sending messages (no expiration)
|
|
56
|
+
|
|
57
|
+
**Key difference:** When you provide an external `url`, we download and process the file on every send.
|
|
58
|
+
When you use a pre-uploaded `attachment_id`, the file is already stored — so repeated sends skip the download step entirely.
|
|
59
|
+
|
|
60
|
+
## Domain Allowlisting
|
|
61
|
+
|
|
62
|
+
Attachment URLs in API responses are served from `cdn.linqapp.com`. This includes:
|
|
63
|
+
- `url` fields in media and voice memo message parts
|
|
64
|
+
- `download_url` fields in attachment and upload response objects
|
|
65
|
+
|
|
66
|
+
If your application enforces domain allowlists (e.g., for SSRF protection), add:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
cdn.linqapp.com
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Supported File Types
|
|
73
|
+
|
|
74
|
+
- **Images:** JPEG, PNG, GIF, HEIC, HEIF, TIFF, BMP
|
|
75
|
+
- **Videos:** MP4, MOV, M4V
|
|
76
|
+
- **Audio:** M4A, AAC, MP3, WAV, AIFF, CAF, AMR
|
|
77
|
+
- **Documents:** PDF, TXT, RTF, CSV, Office formats, ZIP
|
|
78
|
+
- **Contact & Calendar:** VCF, ICS
|
|
79
|
+
|
|
80
|
+
## Audio: Attachment vs Voice Memo
|
|
81
|
+
|
|
82
|
+
Audio files sent as media parts appear as **downloadable file attachments** in iMessage.
|
|
83
|
+
To send audio as an **iMessage voice memo bubble** (with native inline playback UI),
|
|
84
|
+
use the dedicated `POST /v3/chats/{chatId}/voicememo` endpoint instead.
|
|
85
|
+
|
|
86
|
+
## File Size Limits
|
|
87
|
+
|
|
88
|
+
- **URL-based (`url` field):** 10MB maximum
|
|
89
|
+
- **Pre-upload (`attachment_id`):** 100MB maximum
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
@cached_property
|
|
93
|
+
def with_raw_response(self) -> AttachmentsResourceWithRawResponse:
|
|
94
|
+
"""
|
|
95
|
+
This property can be used as a prefix for any HTTP method call to return
|
|
96
|
+
the raw response object instead of the parsed content.
|
|
97
|
+
|
|
98
|
+
For more information, see https://www.github.com/linq-team/linq-python#accessing-raw-response-data-eg-headers
|
|
99
|
+
"""
|
|
100
|
+
return AttachmentsResourceWithRawResponse(self)
|
|
101
|
+
|
|
102
|
+
@cached_property
|
|
103
|
+
def with_streaming_response(self) -> AttachmentsResourceWithStreamingResponse:
|
|
104
|
+
"""
|
|
105
|
+
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
|
|
106
|
+
|
|
107
|
+
For more information, see https://www.github.com/linq-team/linq-python#with_streaming_response
|
|
108
|
+
"""
|
|
109
|
+
return AttachmentsResourceWithStreamingResponse(self)
|
|
110
|
+
|
|
111
|
+
def create(
|
|
112
|
+
self,
|
|
113
|
+
*,
|
|
114
|
+
content_type: SupportedContentType,
|
|
115
|
+
filename: str,
|
|
116
|
+
size_bytes: int,
|
|
117
|
+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
|
118
|
+
# The extra values given here take precedence over values defined on the client or passed to this method.
|
|
119
|
+
extra_headers: Headers | None = None,
|
|
120
|
+
extra_query: Query | None = None,
|
|
121
|
+
extra_body: Body | None = None,
|
|
122
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
123
|
+
) -> AttachmentCreateResponse:
|
|
124
|
+
"""
|
|
125
|
+
**This endpoint is optional.** You can send media by simply providing a URL in
|
|
126
|
+
your message's media part — no pre-upload required. Use this endpoint only when
|
|
127
|
+
you want to upload a file ahead of time for reuse or latency optimization.
|
|
128
|
+
|
|
129
|
+
Returns a presigned upload URL and a permanent `attachment_id` you can reference
|
|
130
|
+
in future messages.
|
|
131
|
+
|
|
132
|
+
## Step 1: Request an upload URL
|
|
133
|
+
|
|
134
|
+
Call this endpoint with file metadata:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
POST /v3/attachments
|
|
138
|
+
{
|
|
139
|
+
"filename": "photo.jpg",
|
|
140
|
+
"content_type": "image/jpeg",
|
|
141
|
+
"size_bytes": 1024000
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The response includes an `upload_url` (valid for 15 minutes) and a permanent
|
|
146
|
+
`attachment_id`.
|
|
147
|
+
|
|
148
|
+
## Step 2: Upload the file
|
|
149
|
+
|
|
150
|
+
Make a PUT request to the `upload_url` with the raw file bytes as the request
|
|
151
|
+
body. You **must** include all headers from `required_headers` exactly as
|
|
152
|
+
returned — the presigned URL is signed with these values and S3 will reject the
|
|
153
|
+
upload if they don't match.
|
|
154
|
+
|
|
155
|
+
The request body is the binary file content — **not** JSON, **not** multipart
|
|
156
|
+
form data. The file must equal `size_bytes` bytes (the value you declared in
|
|
157
|
+
step 1).
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
curl -X PUT "<upload_url from step 1>" \\
|
|
161
|
+
-H "Content-Type: image/jpeg" \\
|
|
162
|
+
-H "Content-Length: 1024000" \\
|
|
163
|
+
--data-binary @photo.jpg
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Step 3: Send a message with the attachment
|
|
167
|
+
|
|
168
|
+
Reference the `attachment_id` in a media part. The ID never expires — use it in
|
|
169
|
+
as many messages as you want.
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
POST /v3/chats
|
|
173
|
+
{
|
|
174
|
+
"from": "+15559876543",
|
|
175
|
+
"to": ["+15551234567"],
|
|
176
|
+
"message": {
|
|
177
|
+
"parts": [
|
|
178
|
+
{ "type": "media", "attachment_id": "<attachment_id from step 1>" }
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## When to use this instead of a URL in the media part
|
|
185
|
+
|
|
186
|
+
- Sending the same file to multiple recipients (avoids re-downloading each time)
|
|
187
|
+
- Large files where you want to separate upload from message send
|
|
188
|
+
- Latency-sensitive sends where the file should already be stored
|
|
189
|
+
|
|
190
|
+
If you just need to send a file once, skip all of this and pass a `url` directly
|
|
191
|
+
in the media part instead.
|
|
192
|
+
|
|
193
|
+
**File Size Limit:** 100MB
|
|
194
|
+
|
|
195
|
+
**Unsupported Types:** WebP, SVG, FLAC, OGG, and executable files are explicitly
|
|
196
|
+
rejected.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
content_type: Supported MIME types for file attachments and media URLs.
|
|
200
|
+
|
|
201
|
+
**Images:** image/jpeg, image/png, image/gif, image/heic, image/heif,
|
|
202
|
+
image/tiff, image/bmp
|
|
203
|
+
|
|
204
|
+
**Videos:** video/mp4, video/quicktime, video/mpeg, video/3gpp
|
|
205
|
+
|
|
206
|
+
**Audio:** audio/mpeg, audio/mp4, audio/x-m4a, audio/x-caf, audio/wav,
|
|
207
|
+
audio/aiff, audio/aac, audio/amr
|
|
208
|
+
|
|
209
|
+
**Documents:** application/pdf, text/plain, text/markdown, text/vcard, text/rtf,
|
|
210
|
+
text/csv, text/html, text/calendar, application/msword,
|
|
211
|
+
application/vnd.openxmlformats-officedocument.wordprocessingml.document,
|
|
212
|
+
application/vnd.ms-excel,
|
|
213
|
+
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,
|
|
214
|
+
application/vnd.ms-powerpoint,
|
|
215
|
+
application/vnd.openxmlformats-officedocument.presentationml.presentation,
|
|
216
|
+
application/vnd.apple.pages, application/vnd.apple.numbers,
|
|
217
|
+
application/vnd.apple.keynote, application/epub+zip, application/zip
|
|
218
|
+
|
|
219
|
+
**Unsupported:** WebP, SVG, FLAC, OGG, and executable files are explicitly
|
|
220
|
+
rejected.
|
|
221
|
+
|
|
222
|
+
filename: Name of the file to upload
|
|
223
|
+
|
|
224
|
+
size_bytes: Size of the file in bytes (max 100MB)
|
|
225
|
+
|
|
226
|
+
extra_headers: Send extra headers
|
|
227
|
+
|
|
228
|
+
extra_query: Add additional query parameters to the request
|
|
229
|
+
|
|
230
|
+
extra_body: Add additional JSON properties to the request
|
|
231
|
+
|
|
232
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
233
|
+
"""
|
|
234
|
+
return self._post(
|
|
235
|
+
"/v3/attachments",
|
|
236
|
+
body=maybe_transform(
|
|
237
|
+
{
|
|
238
|
+
"content_type": content_type,
|
|
239
|
+
"filename": filename,
|
|
240
|
+
"size_bytes": size_bytes,
|
|
241
|
+
},
|
|
242
|
+
attachment_create_params.AttachmentCreateParams,
|
|
243
|
+
),
|
|
244
|
+
options=make_request_options(
|
|
245
|
+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
|
246
|
+
),
|
|
247
|
+
cast_to=AttachmentCreateResponse,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def retrieve(
|
|
251
|
+
self,
|
|
252
|
+
attachment_id: str,
|
|
253
|
+
*,
|
|
254
|
+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
|
255
|
+
# The extra values given here take precedence over values defined on the client or passed to this method.
|
|
256
|
+
extra_headers: Headers | None = None,
|
|
257
|
+
extra_query: Query | None = None,
|
|
258
|
+
extra_body: Body | None = None,
|
|
259
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
260
|
+
) -> AttachmentRetrieveResponse:
|
|
261
|
+
"""
|
|
262
|
+
Retrieve metadata for a specific attachment including its status, file
|
|
263
|
+
information, and URLs for downloading.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
extra_headers: Send extra headers
|
|
267
|
+
|
|
268
|
+
extra_query: Add additional query parameters to the request
|
|
269
|
+
|
|
270
|
+
extra_body: Add additional JSON properties to the request
|
|
271
|
+
|
|
272
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
273
|
+
"""
|
|
274
|
+
if not attachment_id:
|
|
275
|
+
raise ValueError(f"Expected a non-empty value for `attachment_id` but received {attachment_id!r}")
|
|
276
|
+
return self._get(
|
|
277
|
+
path_template("/v3/attachments/{attachment_id}", attachment_id=attachment_id),
|
|
278
|
+
options=make_request_options(
|
|
279
|
+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
|
280
|
+
),
|
|
281
|
+
cast_to=AttachmentRetrieveResponse,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class AsyncAttachmentsResource(AsyncAPIResource):
|
|
286
|
+
"""
|
|
287
|
+
Send files (images, videos, documents, audio) with messages by providing a URL in a media part.
|
|
288
|
+
Pre-uploading via `POST /v3/attachments` is **optional** and only needed for specific optimization scenarios.
|
|
289
|
+
|
|
290
|
+
## Sending Media via URL (up to 10MB)
|
|
291
|
+
|
|
292
|
+
Provide a publicly accessible HTTPS URL with a [supported media type](#supported-file-types) in the `url` field of a media part.
|
|
293
|
+
|
|
294
|
+
```json
|
|
295
|
+
{
|
|
296
|
+
"parts": [
|
|
297
|
+
{ "type": "media", "url": "https://your-cdn.com/images/photo.jpg" }
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
This works with any URL you already host — no pre-upload step required. **Maximum file size: 10MB.**
|
|
303
|
+
|
|
304
|
+
## Pre-Upload (required for files over 10MB)
|
|
305
|
+
|
|
306
|
+
Use `POST /v3/attachments` when you want to:
|
|
307
|
+
- **Send files larger than 10MB** (up to 100MB) — URL-based downloads are limited to 10MB
|
|
308
|
+
- **Send the same file to many recipients** — upload once, reuse the `attachment_id` without re-downloading each time
|
|
309
|
+
- **Reduce message send latency** — the file is already stored, so sending is faster
|
|
310
|
+
|
|
311
|
+
**How it works:**
|
|
312
|
+
1. `POST /v3/attachments` with file metadata → returns a presigned `upload_url` (valid for **15 minutes**) and a permanent `attachment_id`
|
|
313
|
+
2. PUT the raw file bytes to the `upload_url` with the `required_headers` (no JSON or multipart — just the binary content)
|
|
314
|
+
3. Reference the `attachment_id` in your media part when sending messages (no expiration)
|
|
315
|
+
|
|
316
|
+
**Key difference:** When you provide an external `url`, we download and process the file on every send.
|
|
317
|
+
When you use a pre-uploaded `attachment_id`, the file is already stored — so repeated sends skip the download step entirely.
|
|
318
|
+
|
|
319
|
+
## Domain Allowlisting
|
|
320
|
+
|
|
321
|
+
Attachment URLs in API responses are served from `cdn.linqapp.com`. This includes:
|
|
322
|
+
- `url` fields in media and voice memo message parts
|
|
323
|
+
- `download_url` fields in attachment and upload response objects
|
|
324
|
+
|
|
325
|
+
If your application enforces domain allowlists (e.g., for SSRF protection), add:
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
cdn.linqapp.com
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Supported File Types
|
|
332
|
+
|
|
333
|
+
- **Images:** JPEG, PNG, GIF, HEIC, HEIF, TIFF, BMP
|
|
334
|
+
- **Videos:** MP4, MOV, M4V
|
|
335
|
+
- **Audio:** M4A, AAC, MP3, WAV, AIFF, CAF, AMR
|
|
336
|
+
- **Documents:** PDF, TXT, RTF, CSV, Office formats, ZIP
|
|
337
|
+
- **Contact & Calendar:** VCF, ICS
|
|
338
|
+
|
|
339
|
+
## Audio: Attachment vs Voice Memo
|
|
340
|
+
|
|
341
|
+
Audio files sent as media parts appear as **downloadable file attachments** in iMessage.
|
|
342
|
+
To send audio as an **iMessage voice memo bubble** (with native inline playback UI),
|
|
343
|
+
use the dedicated `POST /v3/chats/{chatId}/voicememo` endpoint instead.
|
|
344
|
+
|
|
345
|
+
## File Size Limits
|
|
346
|
+
|
|
347
|
+
- **URL-based (`url` field):** 10MB maximum
|
|
348
|
+
- **Pre-upload (`attachment_id`):** 100MB maximum
|
|
349
|
+
"""
|
|
350
|
+
|
|
351
|
+
@cached_property
|
|
352
|
+
def with_raw_response(self) -> AsyncAttachmentsResourceWithRawResponse:
|
|
353
|
+
"""
|
|
354
|
+
This property can be used as a prefix for any HTTP method call to return
|
|
355
|
+
the raw response object instead of the parsed content.
|
|
356
|
+
|
|
357
|
+
For more information, see https://www.github.com/linq-team/linq-python#accessing-raw-response-data-eg-headers
|
|
358
|
+
"""
|
|
359
|
+
return AsyncAttachmentsResourceWithRawResponse(self)
|
|
360
|
+
|
|
361
|
+
@cached_property
|
|
362
|
+
def with_streaming_response(self) -> AsyncAttachmentsResourceWithStreamingResponse:
|
|
363
|
+
"""
|
|
364
|
+
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
|
|
365
|
+
|
|
366
|
+
For more information, see https://www.github.com/linq-team/linq-python#with_streaming_response
|
|
367
|
+
"""
|
|
368
|
+
return AsyncAttachmentsResourceWithStreamingResponse(self)
|
|
369
|
+
|
|
370
|
+
async def create(
|
|
371
|
+
self,
|
|
372
|
+
*,
|
|
373
|
+
content_type: SupportedContentType,
|
|
374
|
+
filename: str,
|
|
375
|
+
size_bytes: int,
|
|
376
|
+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
|
377
|
+
# The extra values given here take precedence over values defined on the client or passed to this method.
|
|
378
|
+
extra_headers: Headers | None = None,
|
|
379
|
+
extra_query: Query | None = None,
|
|
380
|
+
extra_body: Body | None = None,
|
|
381
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
382
|
+
) -> AttachmentCreateResponse:
|
|
383
|
+
"""
|
|
384
|
+
**This endpoint is optional.** You can send media by simply providing a URL in
|
|
385
|
+
your message's media part — no pre-upload required. Use this endpoint only when
|
|
386
|
+
you want to upload a file ahead of time for reuse or latency optimization.
|
|
387
|
+
|
|
388
|
+
Returns a presigned upload URL and a permanent `attachment_id` you can reference
|
|
389
|
+
in future messages.
|
|
390
|
+
|
|
391
|
+
## Step 1: Request an upload URL
|
|
392
|
+
|
|
393
|
+
Call this endpoint with file metadata:
|
|
394
|
+
|
|
395
|
+
```json
|
|
396
|
+
POST /v3/attachments
|
|
397
|
+
{
|
|
398
|
+
"filename": "photo.jpg",
|
|
399
|
+
"content_type": "image/jpeg",
|
|
400
|
+
"size_bytes": 1024000
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
The response includes an `upload_url` (valid for 15 minutes) and a permanent
|
|
405
|
+
`attachment_id`.
|
|
406
|
+
|
|
407
|
+
## Step 2: Upload the file
|
|
408
|
+
|
|
409
|
+
Make a PUT request to the `upload_url` with the raw file bytes as the request
|
|
410
|
+
body. You **must** include all headers from `required_headers` exactly as
|
|
411
|
+
returned — the presigned URL is signed with these values and S3 will reject the
|
|
412
|
+
upload if they don't match.
|
|
413
|
+
|
|
414
|
+
The request body is the binary file content — **not** JSON, **not** multipart
|
|
415
|
+
form data. The file must equal `size_bytes` bytes (the value you declared in
|
|
416
|
+
step 1).
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
curl -X PUT "<upload_url from step 1>" \\
|
|
420
|
+
-H "Content-Type: image/jpeg" \\
|
|
421
|
+
-H "Content-Length: 1024000" \\
|
|
422
|
+
--data-binary @photo.jpg
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
## Step 3: Send a message with the attachment
|
|
426
|
+
|
|
427
|
+
Reference the `attachment_id` in a media part. The ID never expires — use it in
|
|
428
|
+
as many messages as you want.
|
|
429
|
+
|
|
430
|
+
```json
|
|
431
|
+
POST /v3/chats
|
|
432
|
+
{
|
|
433
|
+
"from": "+15559876543",
|
|
434
|
+
"to": ["+15551234567"],
|
|
435
|
+
"message": {
|
|
436
|
+
"parts": [
|
|
437
|
+
{ "type": "media", "attachment_id": "<attachment_id from step 1>" }
|
|
438
|
+
]
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## When to use this instead of a URL in the media part
|
|
444
|
+
|
|
445
|
+
- Sending the same file to multiple recipients (avoids re-downloading each time)
|
|
446
|
+
- Large files where you want to separate upload from message send
|
|
447
|
+
- Latency-sensitive sends where the file should already be stored
|
|
448
|
+
|
|
449
|
+
If you just need to send a file once, skip all of this and pass a `url` directly
|
|
450
|
+
in the media part instead.
|
|
451
|
+
|
|
452
|
+
**File Size Limit:** 100MB
|
|
453
|
+
|
|
454
|
+
**Unsupported Types:** WebP, SVG, FLAC, OGG, and executable files are explicitly
|
|
455
|
+
rejected.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
content_type: Supported MIME types for file attachments and media URLs.
|
|
459
|
+
|
|
460
|
+
**Images:** image/jpeg, image/png, image/gif, image/heic, image/heif,
|
|
461
|
+
image/tiff, image/bmp
|
|
462
|
+
|
|
463
|
+
**Videos:** video/mp4, video/quicktime, video/mpeg, video/3gpp
|
|
464
|
+
|
|
465
|
+
**Audio:** audio/mpeg, audio/mp4, audio/x-m4a, audio/x-caf, audio/wav,
|
|
466
|
+
audio/aiff, audio/aac, audio/amr
|
|
467
|
+
|
|
468
|
+
**Documents:** application/pdf, text/plain, text/markdown, text/vcard, text/rtf,
|
|
469
|
+
text/csv, text/html, text/calendar, application/msword,
|
|
470
|
+
application/vnd.openxmlformats-officedocument.wordprocessingml.document,
|
|
471
|
+
application/vnd.ms-excel,
|
|
472
|
+
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,
|
|
473
|
+
application/vnd.ms-powerpoint,
|
|
474
|
+
application/vnd.openxmlformats-officedocument.presentationml.presentation,
|
|
475
|
+
application/vnd.apple.pages, application/vnd.apple.numbers,
|
|
476
|
+
application/vnd.apple.keynote, application/epub+zip, application/zip
|
|
477
|
+
|
|
478
|
+
**Unsupported:** WebP, SVG, FLAC, OGG, and executable files are explicitly
|
|
479
|
+
rejected.
|
|
480
|
+
|
|
481
|
+
filename: Name of the file to upload
|
|
482
|
+
|
|
483
|
+
size_bytes: Size of the file in bytes (max 100MB)
|
|
484
|
+
|
|
485
|
+
extra_headers: Send extra headers
|
|
486
|
+
|
|
487
|
+
extra_query: Add additional query parameters to the request
|
|
488
|
+
|
|
489
|
+
extra_body: Add additional JSON properties to the request
|
|
490
|
+
|
|
491
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
492
|
+
"""
|
|
493
|
+
return await self._post(
|
|
494
|
+
"/v3/attachments",
|
|
495
|
+
body=await async_maybe_transform(
|
|
496
|
+
{
|
|
497
|
+
"content_type": content_type,
|
|
498
|
+
"filename": filename,
|
|
499
|
+
"size_bytes": size_bytes,
|
|
500
|
+
},
|
|
501
|
+
attachment_create_params.AttachmentCreateParams,
|
|
502
|
+
),
|
|
503
|
+
options=make_request_options(
|
|
504
|
+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
|
505
|
+
),
|
|
506
|
+
cast_to=AttachmentCreateResponse,
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
async def retrieve(
|
|
510
|
+
self,
|
|
511
|
+
attachment_id: str,
|
|
512
|
+
*,
|
|
513
|
+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
|
514
|
+
# The extra values given here take precedence over values defined on the client or passed to this method.
|
|
515
|
+
extra_headers: Headers | None = None,
|
|
516
|
+
extra_query: Query | None = None,
|
|
517
|
+
extra_body: Body | None = None,
|
|
518
|
+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
|
|
519
|
+
) -> AttachmentRetrieveResponse:
|
|
520
|
+
"""
|
|
521
|
+
Retrieve metadata for a specific attachment including its status, file
|
|
522
|
+
information, and URLs for downloading.
|
|
523
|
+
|
|
524
|
+
Args:
|
|
525
|
+
extra_headers: Send extra headers
|
|
526
|
+
|
|
527
|
+
extra_query: Add additional query parameters to the request
|
|
528
|
+
|
|
529
|
+
extra_body: Add additional JSON properties to the request
|
|
530
|
+
|
|
531
|
+
timeout: Override the client-level default timeout for this request, in seconds
|
|
532
|
+
"""
|
|
533
|
+
if not attachment_id:
|
|
534
|
+
raise ValueError(f"Expected a non-empty value for `attachment_id` but received {attachment_id!r}")
|
|
535
|
+
return await self._get(
|
|
536
|
+
path_template("/v3/attachments/{attachment_id}", attachment_id=attachment_id),
|
|
537
|
+
options=make_request_options(
|
|
538
|
+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
|
539
|
+
),
|
|
540
|
+
cast_to=AttachmentRetrieveResponse,
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
class AttachmentsResourceWithRawResponse:
|
|
545
|
+
def __init__(self, attachments: AttachmentsResource) -> None:
|
|
546
|
+
self._attachments = attachments
|
|
547
|
+
|
|
548
|
+
self.create = to_raw_response_wrapper(
|
|
549
|
+
attachments.create,
|
|
550
|
+
)
|
|
551
|
+
self.retrieve = to_raw_response_wrapper(
|
|
552
|
+
attachments.retrieve,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
class AsyncAttachmentsResourceWithRawResponse:
|
|
557
|
+
def __init__(self, attachments: AsyncAttachmentsResource) -> None:
|
|
558
|
+
self._attachments = attachments
|
|
559
|
+
|
|
560
|
+
self.create = async_to_raw_response_wrapper(
|
|
561
|
+
attachments.create,
|
|
562
|
+
)
|
|
563
|
+
self.retrieve = async_to_raw_response_wrapper(
|
|
564
|
+
attachments.retrieve,
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
class AttachmentsResourceWithStreamingResponse:
|
|
569
|
+
def __init__(self, attachments: AttachmentsResource) -> None:
|
|
570
|
+
self._attachments = attachments
|
|
571
|
+
|
|
572
|
+
self.create = to_streamed_response_wrapper(
|
|
573
|
+
attachments.create,
|
|
574
|
+
)
|
|
575
|
+
self.retrieve = to_streamed_response_wrapper(
|
|
576
|
+
attachments.retrieve,
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
class AsyncAttachmentsResourceWithStreamingResponse:
|
|
581
|
+
def __init__(self, attachments: AsyncAttachmentsResource) -> None:
|
|
582
|
+
self._attachments = attachments
|
|
583
|
+
|
|
584
|
+
self.create = async_to_streamed_response_wrapper(
|
|
585
|
+
attachments.create,
|
|
586
|
+
)
|
|
587
|
+
self.retrieve = async_to_streamed_response_wrapper(
|
|
588
|
+
attachments.retrieve,
|
|
589
|
+
)
|