Skip to main content
CometChat.createUploadFileRequest(receiverId, receiverType) returns an UploadFileRequest — the entry point for uploading files directly to storage with per-file progress, success, and failure. Upload is decoupled from sending: each uploaded file yields an Attachment (carrying a hosted URL), which you then attach to a MediaMessage and send with sendMediaMessage(). A request object is scoped to one destination (receiverId / receiverType) and one upload batch. This is the recommended way to build a multi-attachment composer: create a request, upload a batch of files, show a progress bar per file, let the user remove or retry individual files, then send them as a single media message with multiple attachments (or split across several).
Why upload separately instead of passing files to sendMediaMessage()?The classic path (passing a files list straight to the MediaMessage constructor) uploads and sends in one blocking call — you get no progress, no per-file remove, and no per-file retry. An UploadFileRequest moves the upload out of the send call so you can drive a rich composer UI, then send instantly because the files are already hosted.

The upload-then-send flow

1

Create a request

Call CometChat.createUploadFileRequest(receiverId, receiverType). The recipient is required — the server uses it to apply role- and scope-based access control before issuing upload URLs.
2

Upload

Call request.uploadAttachments(items, listener), where each UploadAttachment pairs a file with an app-supplied fileId (required) that is echoed back on every event, so you can map callbacks to your UI rows. Validation, presigning, and the byte transfer run asynchronously and report through the listener.
3

Track progress & handle failures

Your UploadFileListener receives onFileProgress per file, then onFileUploaded (success), onFileError (rejected — not retryable), or onFileFailure (failed — retryable). Use request.removeAttachment() or request.retryAttachment() as the user acts.
4

Collect attachments

Each onFileUploaded hands you an Attachment with a hosted URL. You can also read them from the request at any time with request.getAttachments().
5

Build & send the message

Put the attachments on a MediaMessage with attachments: and call sendMediaMessage(). Because the attachments already have URLs, the message is sent as JSON — no re-upload.
6

Clean up

Call request.clearAll() after a successful send (or to abandon the composer) to release the batch from memory.

Create an upload request

Create an UploadFileRequest scoped to one destination, then upload files into it. The request owns a single batch — every file shares one batchId — and you pair each file with a fileId you choose, so you can correlate progress and errors back to your own UI.
Call uploadAttachments again on the same request to add more files to the same batch (the user picks more, pastes, or drags another in). For a single file, request.uploadAttachment(fileId, file, listener) is the convenience form. On web — or anywhere you only hold bytes, such as a clipboard paste — build the file from bytes instead:
size must be accurate — the storage upload policy allows only a ±10% tolerance, and a file with an unknown or zero size is rejected before any upload starts.

Configure the request

receiverId / receiverType are fixed when you create the request; the server authorizes every upload against that conversation once role-based access control is enabled. The rest are optional and must be set before the batch is first used (they throw once it is):

Track progress and completion

Pass an UploadFileListener to uploadAttachments (it receives that call’s files), or register a batch-wide one with request.addUploadListener(...). UploadFileListener ships default no-op callbacks, so extend it and override only the ones you need.
onFileError and onFileFailure are deliberately different: an error will never succeed as-is, so surface it and let the user remove the file; a failure is worth a retry. onComplete fires when the batch drains — nothing left in flight. You can also poll the batch at any point instead of tracking events:

Retry, remove, and clear

removeAttachment is safe in any state and frees the file’s slot against file.count.max, so the user can pick a replacement. retryAttachment is a no-op on a rejected file (onFileError — not retryable).

Read the limits

Use getMaxAttachmentCount() to cap your picker so the user cannot select more than the message allows, and getMaxAttachmentSize() to reject an over-sized file before uploading it.

Send the uploaded attachments

Attach the Attachment objects the batch produced — the ones you kept from onFileUploaded, or request.getAttachments() — to one MediaMessage. Use attachments (not files — the bytes are already on storage), and set type to the kind those attachments are. Call request.clearAll() once the message is sent to release the batch.
Keep one message to a single kind. type is what a receiving UI picks its bubble from, so images in a message typed file render as file cards rather than a grid. If the user picked a mix, group the attachments by kind and send one message per kind — this is exactly what the UI Kit’s composer does, tagging each message with a shared batchId so they display as one group.

Error handling

The non-retryable codes arrive via onFileError, the retryable ones via onFileFailure.
Upload state is in-memory and does not survive the app being killed — a batch cannot be resumed after a restart. A file with no progress for 30 seconds is treated as stalled and failed (retryable).

Next Steps

Send A Message

Send text, media, and custom messages

Multiple Attachments

Send several attachments in one media message

Receive Messages

Listen for incoming messages in real-time

Threaded Messages

Upload into a thread with setParentMessageId