Overview
The attachment system provides:- Direct upload to Cloudflare R2 storage
- Presigned URL generation for secure uploads
- Support for all file types (images, videos, documents, archives)
- Status tracking (uploading, complete, failed)
- Automatic linking to messages
- Organization and channel-scoped storage
Direct Uploads
Files upload directly to R2 storage without routing through the backend
Presigned URLs
Secure, time-limited upload URLs with 5-minute expiration
Status Tracking
Monitor upload progress with uploading, complete, and failed states
Public Access
Files are stored with public-read ACL for easy sharing
Uploading Files
Attach to Messages
Upload files when composing messages:- Click the attachment icon (📎) in the message composer
- Select one or more files from your computer
- Files begin uploading immediately
- Upload progress is shown for each file
- Once complete, add your message text
- Send the message with attachments
Direct Upload Flow
The upload process works in multiple stages: Stage 1: Request Presigned URLuploadUrl- Presigned S3 PUT URLkey- Attachment ID (used as storage key)publicUrl- Public access URL after uploadresourceId- Attachment ID for linking to messages
Presigned URLs expire after 5 minutes. If your upload takes longer, request a new presigned URL.
Upload Failure Handling
If an upload fails:- The client detects the failure (network error, timeout, etc.)
- Call
attachment.failRPC with the attachment ID: - Attachment status is set to “failed”
- User can retry the upload
Supported File Types
All file types are supported: Documents:- PDF documents
- Microsoft Office files (Word, Excel, PowerPoint)
- Text files and code
- JPEG, PNG, GIF, WebP
- SVG and other vector formats
- Raw image formats
- MP4, WebM, MOV
- Other common video formats
- ZIP, TAR, GZ
- RAR and 7Z
- Audio files
- Executable files
- Any other file type
File Size Limits
Upload limits:- Default: No hard limit in the application
- Practical: Limited by browser memory and R2 storage quotas
- Recommended: Keep files under 100MB for best performance
Storage Structure
Files are stored in Cloudflare R2 with the following structure:Accessing Files
Files are publicly accessible via their public URL:Generating Public URLs
IfS3_PUBLIC_URL environment variable is configured:
Deleting Attachments
Remove attachments from the system:- Hover over an attachment in a message
- Click the Delete button
- Confirm deletion
- Sets
deletedAttimestamp - Attachment no longer appears in UI
- File remains in R2 storage (for recovery)
Only the uploader or organization admins can delete attachments.
Other Upload Types
The presign endpoint supports multiple upload types:User Avatars
avatars/{userId}/{randomId}
Bot Avatars
avatars/bots/{botId}/{randomId}
Organization Logos
avatars/organizations/{orgId}/{randomId}
Custom Emoji
emojis/{organizationId}/{randomId}
Rate Limiting
Avatar and emoji uploads are rate-limited:- 5 uploads per hour per user
- Prevents abuse and excessive storage usage
- Applies to all avatar and emoji uploads
- Attachment uploads are not rate-limited
Technical Details
Attachment Data Model
Database Indexes
Optimized queries with indexes on:organization_id- Organization-scoped fileschannel_id- Channel attachmentsmessage_id- Message attachmentsmessage_id, uploaded_at- Chronological message attachmentsuploaded_by- User’s uploadsstatus- Filter by upload statusdeleted_at- Exclude deleted files
S3/R2 Configuration
Environment variables:Presigned URL Generation
Presigned URLs are generated with:- Method: PUT
- ACL: public-read
- Content-Type: Specified in request
- Expiration: 300 seconds (5 minutes)
Permissions
Upload Attachments:- Must be organization member
- Must have access to the specified channel
- Original uploader
- Organization admin/owner
- Only the original uploader
Security Considerations
- Presigned URLs are time-limited (5 minutes)
- Only authenticated users can request presigned URLs
- Upload permissions validated before URL generation
- Files stored with public-read for easy sharing
- No direct backend upload path (prevents server load)