Attachment Resource
An attachment represents a file uploaded to Duvet, either attached to a message or used as a space emoji/sticker.
Attachment Object
| Field | Type | Description |
|---|---|---|
id | string | The attachment's UUID (v7, time-ordered) |
message_id | string? | The message this attachment belongs to (null for space emoji/sticker attachments) |
filename | string | Original filename |
content_type | string | MIME type of the file |
size_bytes | integer | File size in bytes |
width | integer? | Image or video width in pixels |
height | integer? | Image or video height in pixels |
created_at | ISO 8601 | When the attachment was created |
deleted_at | ISO 8601? | Soft delete timestamp (present when attachment has been deleted) |
url | string | Signed download URL with HMAC signature and expiry. Only present on message attachments. |
Fields
r2_key,owner_id,storage_bucket_id,updated_at, andexpires_atare hidden from API responses.
Message attachments require a valid signed URL (url field) to download. Non-message attachments (avatars, emojis, stickers) can be accessed directly via /attachments/{id} without a signature.
Example
{
"id": "019cf96a-4e2a-73b1-9a0f-2b8c5d1e7f34",
"message_id": "019cf96a-3d5b-72c5-830f-75e85355d078",
"filename": "screenshot.png",
"content_type": "image/png",
"size_bytes": 245760,
"width": 1920,
"height": 1080,
"created_at": "2025-10-30T00:25:00.000Z",
"deleted_at": null,
"url": "/attachments/019cf96a-4e2a-73b1-9a0f-2b8c5d1e7f34?sig=a1b2c3d4...&exp=1735689600"
} Downloading Attachments
/attachments/{attachmentId}Download an attachment. Message attachments require a valid signature (sig and exp query params). Non-message attachments (avatars, emojis, stickers) can be accessed without a signature. Returns a 307 redirect to a presigned R2/S3 URL (1 hour expiry) that supports Range requests.
| Field | Type | Description |
|---|---|---|
sig | string | HMAC-SHA256 signature (from the url field) |
exp | string | Unix timestamp expiry (from the url field) |
stream ? | string | Set to 1 to stream the file directly through the server instead of redirecting (bypasses CORS restrictions) |
Re-signing Expired URLs
/attachments/{attachmentId}/resignGenerate a fresh signed URL for an attachment. Use when the original signed URL is expiring. Requires authentication.
Returns: {"url": "/attachments/{id}?sig=...&exp=..."} with a new 24-hour TTL.