File Upload
The Lovina Chat SDK supports sending files (images, documents, audio, video) through the chat widget. Users can attach files via the paperclip button in the message composer.
Supported File Types and Size Limits
| Constraint | Limit |
|---|---|
| Max files per message | 5 |
| Max file size | 10 MB per file |
| Accepted types | image/*, application/pdf, text/*, audio/*, video/* |
Enabling / Disabling File Upload
File upload is enabled by default on web. To disable it:
window.lovinaSettings = {
fileUploadEnabled: false,
};
On native SDKs, the fileUploadEnabled flag is injected automatically based on WebView capabilities.
How It Works
User Experience
- Attach files — User taps the paperclip icon in the composer to open the native file picker.
- Preview — Selected files appear in a horizontal preview strip below the composer. Images show thumbnails; other files show a file icon with extension.
- Remove — Each preview has an "x" button to remove a file before sending.
- Send — User taps send. Files are uploaded, then the message is sent with attachment references.
- Display — Images render inline in the message bubble. Other files show as download links with file icon and size.
Upload Flow
The SDK uses a two-step upload flow:
1. User picks file(s) via native file picker
2. Files validated client-side (size + count limits)
3. Files staged in preview strip with blob URL thumbnails
4. On send:
a. Upload each file: POST /sdk/chat/upload
→ Returns { attachment_id, file_url }
b. Send message: POST /sdk/chat/message
→ Body includes { message, attachment_ids: ["att-1", "att-2"] }
c. AI responds via SSE stream
5. On partial failure:
- Successfully uploaded files are still attached
- Failed files show individual error toasts
- Message sends with whatever files succeeded
6. On total failure: message marked as failed with Retry button
Upload API
Step 1: Upload File
POST /sdk/chat/upload
Authorization: Bearer <websiteToken>
X-Session-Token: <session_token>
Content-Type: multipart/form-data
Fields:
session_id string Active session ID
file File The file to upload (max 10MB)
Response:
{
"attachment_id": "019078c3-d4e5-7f01-2345-678901abcdef",
"file_url": "https://storage.example.com/chat-attachments/.../document.pdf",
"file_name": "document.pdf",
"content_type": "application/pdf",
"size_in_bytes": 102400
}
Step 2: Send Message with Attachments
POST /sdk/chat/message
Authorization: Bearer <websiteToken>
Content-Type: application/json
{
"agent_config_id": "agent-xxx",
"message": "Here is the file",
"session_id": "session-xxx",
"attachment_ids": ["019078c3-d4e5-7f01-2345-678901abcdef"]
}
Attachment Object (in messages)
| Field | Type | Description |
|---|---|---|
id | string | Unique attachment ID |
fileType | string | Category: 'image', 'file', 'audio', 'video' |
dataUrl | string | URL to download/view the file |
thumbUrl | string | Thumbnail URL (images only) |
fileSize | number | File size in bytes |
Error Handling
| Scenario | What User Sees |
|---|---|
| File > 10MB | Toast: "filename exceeds 10MB limit" — file rejected |
| > 5 files | Toast: "Maximum 5 files allowed" — extras rejected |
| Upload fails (network) | Toast: "File upload failed. filename" — message sends without that file |
| Upload fails (401) | Toast: "Authentication expired" |
| All uploads fail | Text message still sends (without attachments) |
| Retry | Failed messages show red bubble with Retry button. Files are preserved for re-upload. |
Platform Support
| Platform | Status | Notes |
|---|---|---|
| Web (all browsers) | Supported | Native <input type="file"> with preview strip |
| Android WebView | Supported | onShowFileChooser + ActivityResultLauncher |
| iOS WKWebView | Supported | WKUIDelegate + UIDocumentPickerViewController |
| React Native | Supported | allowFileAccess props on WebView (v11+) |
| Flutter | Supported | webview_flutter v4+ handles natively |
File upload works on all platforms. The attach button is automatically hidden on platforms where file upload is not available (controlled by fileUploadEnabled flag injected by native SDKs).
How It Works on Each Platform
When a user taps the paperclip button, the widget triggers <input type="file">. Each platform handles this differently:
| Platform | What Opens | Permissions Needed |
|---|---|---|
| Web | Browser file dialog | None |
| Android | System file picker (ACTION_GET_CONTENT) | None (system picker handles its own) |
| iOS | UIDocumentPickerViewController | None (document picker is sandboxed) |
| React Native | Native file picker via react-native-webview | None |
| Flutter | Native file picker via webview_flutter | None |
:::warning Important for Native SDK Developers
Android: Always call onReceiveValue() — even on cancel — or the WebView file input will hang permanently.
iOS: Always call completionHandler() — even on cancel — or WKWebView's file input silently breaks.
:::
Permission Handling
File upload uses system file pickers on all platforms, which means no runtime permissions are required from the user. The OS handles file access internally.
However, there are edge cases:
| Scenario | What Happens | User Sees |
|---|---|---|
| Android: no file manager app | Intent.createChooser shows empty list | "No apps can perform this action" dialog |
| iOS: iCloud file not downloaded | OS downloads file first | iOS download progress indicator |
| iOS: photo library (future camera feature) | Needs NSPhotoLibraryUsageDescription in Info.plist | iOS permission prompt |
| Android < 13: restricted storage | System picker still works | Normal picker (system handles permissions) |
| WebView not configured | <input type="file"> click does nothing | Attach button hidden via fileUploadEnabled: false |
| User cancels picker | onReceiveValue(null) / completionHandler(nil) called | Nothing happens, widget stays functional |
:::info No permissions needed Unlike camera or microphone access, file upload via system pickers does not require any runtime permission prompts. The user simply picks a file from the system UI and the OS grants temporary read access to that specific file. :::