AI Integration Quick Reference
AI Integration Quick Reference
autoSendPendingOnReconnect on, the default). This page shows the offline-aware send listener, how to read a message’s status, how to inspect and recover the send queue, and the queue limits.
Offline support is off by default and requires SDK 5.1.0 or newer. Queuing only happens once you enable it; with offline off, a send over no connection fails exactly as before.
Send a Message
Pass aMessageSentListener to sendMessage. Its lifecycle is normally onMessagePending first (the message is durably queued), then its outcome: onMessageSent (the server accepted it) or onError. Offline, onError is a queued-not-failed notice and onMessageSent still fires later on reconnect, so both can fire for one send (see below). If the message cannot be queued at all, for example because its media could not be copied (code ERROR_OFFLINE_MEDIA_COPY_FAILED, reason PERMANENT), only onError fires.
- Java
- Kotlin
MessageSentListener is accepted by all four persistent send methods:
- Java
- Kotlin
Queued, Not Failed
When the device is offline, the send still firesonMessagePending, then fires onError with the code ERROR_INTERNET_UNAVAILABLE for that attempt. This is a notification, not a failure: the message stays durably PENDING, and onMessageSent fires later, on reconnect, when it actually goes. This is the default behaviour, with autoSendPendingOnReconnect on. With it off, the send is still queued first (onMessagePending fires), then the immediate attempt fails as FAILED (TRANSIENT); recover it with retryMessage.
So for a single send, onError and onMessageSent can both fire (offline, then reconnect), and onError does not always mean the message failed. Always read message.getStatus() to tell the two apart:
- Java
- Kotlin
Message status and failure reason
Every message exposes its offline state.getLocalMessageId() returns the SDK-generated handle that is stamped at compose (while offline is running) and stays stable across the whole lifecycle — use it to retry or discard the message.
MessageFailureReason values:
Inspect the Queue
getPendingMessages returns the entire un-acked outbox — every PENDING, SENDING, and FAILED message, ordered by local compose time. Sent messages are not included. Read each message’s status to group them.
- Java
- Kotlin
Observe the queue app-wide
The per-callMessageSentListener lives only as long as the call. To watch every queue transition — a live send, a reconnect auto-flush, a manual retry, or a resumed send after an app restart — register a global OfflineMessageListener. Its failed callback carries the MessageFailureReason.
- Java
- Kotlin
Retry and Discard
There is no automatic retry of aFAILED message — recovery is explicit and user-invoked. A FAILED message stays in the outbox until you retry or discard it. Use the message’s localMessageId.
retryMessage(localMessageId, listener)re-attempts one message. AFAILEDmessage is reset toPENDINGand sent; aPENDINGmessage is attempted now rather than waiting for a reconnect. The listener receives that attempt’s outcome.retryAllFailedMessages(listener)retries transient failures only; permanent ones are skipped.discardLocalMessage(localMessageId, listener)deletes an unsent (PENDINGorFAILED) message from the device, including its copied media.onSuccessreceives thelocalMessageId.
- Java
- Kotlin
retryMessage and discardLocalMessage reject, changing nothing, when:
Auto-send on Reconnect
By default, queuedPENDING messages are sent automatically when the realtime connection is re-established. This is controlled by OfflineConfig.autoSendPendingOnReconnect (default true); see Enable Offline Support and the reference. A queued message that keeps getting no answer is retried on each reconnect, up to 5 times, then becomes FAILED (TRANSIENT). With it off, nothing retries a queued message on reconnect — a send that gets no answer becomes FAILED (TRANSIENT), which you recover with retryMessage.
Reconnect also runs a catch-up sync for received messages — see Stay in Sync.
Queue Limits
The outbox holds at mostmaxPendingMessages messages still trying to send (PENDING + SENDING); FAILED messages do not count. When the queue is full, OfflineConfig.pendingQueueOverflowPolicy decides what happens:
A media send whose bytes would push the media cache over
maxMediaCacheSize is refused with reason BYTE_OVERFLOW; nothing is evicted to make room.
A refused send (QUEUE_FULL / BYTE_OVERFLOW) is never queued, so it fires no onMessagePending; it reports onError with the code ERROR_PENDING_QUEUE_LIMIT, and the global OfflineMessageListener receives onOutboxMessageFailed(message, reason) carrying the exact reason.
Edits, Deletes, and Reactions Need a Connection
Offline queuing applies to sending new messages only. Changes to existing messages — edit, delete, add or remove a reaction — and other own actions are not queued offline. They are sent to the server whenever you trigger them, and they report the not-connected error if the request gets no answer. Their effect reaches the cache only once the server accepts them.Durable Media Uploads
A multi-attachment upload survives an app restart, but it never resumes on its own. Fetch the existing request for a conversation withgetExistingUploadFileRequest, and read each file’s progress through getItems(), which returns a list of UploadFileItemStatus (each with a State: QUEUED, UPLOADING, UPLOADED, FAILED, or REJECTED, which cannot be retried). Retry an individual file with retryAttachment(fileId).
- Java
- Kotlin