## Pre-upload a file `client.Attachments.New(ctx, body) (*AttachmentNewResponse, error)` **post** `/v3/attachments` **This endpoint is optional.** You can send media by simply providing a URL in your message's media part — no pre-upload required. Use this endpoint only when you want to upload a file ahead of time for reuse or latency optimization. Returns a presigned upload URL and a permanent `attachment_id` you can reference in future messages. ## Step 1: Request an upload URL Call this endpoint with file metadata: ```json POST /v3/attachments { "filename": "photo.jpg", "content_type": "image/jpeg", "size_bytes": 1024000 } ``` The response includes an `upload_url` (valid for 15 minutes) and a permanent `attachment_id`. ## Step 2: Upload the file Make a PUT request to the `upload_url` with the raw file bytes as the request body. You **must** include all headers from `required_headers` exactly as returned — the presigned URL is signed with these values and S3 will reject the upload if they don't match. The request body is the binary file content — **not** JSON, **not** multipart form data. The file must equal `size_bytes` bytes (the value you declared in step 1). ```bash curl -X PUT "" \ -H "Content-Type: image/jpeg" \ -H "Content-Length: 1024000" \ --data-binary @photo.jpg ``` ## Step 3: Send a message with the attachment Reference the `attachment_id` in a media part. The ID never expires — use it in as many messages as you want. ```json POST /v3/chats { "from": "+15559876543", "to": ["+15551234567"], "message": { "parts": [ { "type": "media", "attachment_id": "" } ] } } ``` ## When to use this instead of a URL in the media part - Sending the same file to multiple recipients (avoids re-downloading each time) - Large files where you want to separate upload from message send - Latency-sensitive sends where the file should already be stored If you just need to send a file once, skip all of this and pass a `url` directly in the media part instead. **File Size Limit:** 100MB **Unsupported Types:** WebP, SVG, FLAC, OGG, and executable files are explicitly rejected. ### Parameters - `body AttachmentNewParams` - `ContentType param.Field[SupportedContentType]` Supported MIME types for file attachments and media URLs. **Images:** image/jpeg, image/png, image/gif, image/heic, image/heif, image/tiff, image/bmp, image/svg+xml, image/webp, image/x-icon **Videos:** video/mp4, video/quicktime, video/mpeg, video/mpeg2, video/x-msvideo, video/3gpp **Audio:** audio/mpeg, audio/x-m4a, audio/x-caf, audio/x-wav, audio/x-aiff, audio/aac, audio/midi, audio/amr **Documents:** application/pdf, text/plain, text/markdown, text/vcard, text/rtf, text/csv, text/html, text/calendar, text/xml, application/json, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/x-iwork-pages-sffpages, application/x-iwork-numbers-sffnumbers, application/x-iwork-keynote-sffkey, application/epub+zip, application/zip, application/x-gzip **Transcoded on delivery:** - `audio/x-caf` — CAF files are transcoded to `audio/mp4` for delivery. **Deprecated (accepted but transcoded):** - `audio/mp3` — Deprecated. Use `audio/mpeg` instead. Files sent as audio/mp3 will be delivered as audio/mpeg. - `audio/mp4` — Deprecated. Use `audio/x-m4a` instead. Files sent as audio/mp4 will be delivered as audio/x-m4a. - `audio/aiff` — Deprecated. Use `audio/x-aiff` instead. Files sent as audio/aiff will be delivered as audio/x-aiff. - `image/tiff` — Accepted, but TIFF images are transcoded to JPEG for delivery. **Unsupported:** FLAC, OGG, and executable files are explicitly rejected. - `Filename param.Field[string]` Name of the file to upload - `SizeBytes param.Field[int64]` Size of the file in bytes (max 100MB) ### Returns - `type AttachmentNewResponse struct{…}` - `AttachmentID string` Unique identifier for the attachment (for status checks via GET /v3/attachments/{id}) - `DownloadURL string` Permanent CDN URL for the file. Does not expire. Use the `attachment_id` to reference this file in media parts when sending messages. - `ExpiresAt Time` When the upload URL expires (15 minutes from now) - `HTTPMethod AttachmentNewResponseHTTPMethod` HTTP method to use for upload (always PUT) - `const AttachmentNewResponseHTTPMethodPut AttachmentNewResponseHTTPMethod = "PUT"` - `RequiredHeaders map[string, string]` HTTP headers that must be set on the upload request. The presigned URL is signed with these exact values — S3 will reject the upload if they don't match. - `UploadURL string` Presigned URL for uploading the file. PUT the raw binary file content to this URL with the `required_headers`. Do not JSON-encode or multipart-wrap the body. Expires after 15 minutes. ### Example ```go package main import ( "context" "fmt" "github.com/linq-team/linq-go" "github.com/linq-team/linq-go/option" ) func main() { client := linqgo.NewClient( option.WithAPIKey("My API Key"), ) attachment, err := client.Attachments.New(context.TODO(), linqgo.AttachmentNewParams{ ContentType: linqgo.SupportedContentTypeImageJpeg, Filename: "photo.jpg", SizeBytes: 1024000, }) if err != nil { panic(err.Error()) } fmt.Printf("%+v\n", attachment.AttachmentID) } ``` #### Response ```json { "attachment_id": "550e8400-e29b-41d4-a716-446655440000", "upload_url": "https://uploads.linqapp.com/attachments/550e8400?X-Amz-Algorithm=AWS4-HMAC-SHA256&...", "download_url": "https://cdn.linqapp.com/uploads/partner-id/550e8400/photo.jpg", "http_method": "PUT", "expires_at": "2024-01-15T10:45:00Z", "required_headers": { "Content-Type": "image/jpeg", "Content-Length": "1024000" } } ```