Skip to content
MessageBuilder
Documentation menu
Streaming rich response

Streaming rich response

Send an AIRich placeholder, replace blocks by ID, then synchronize with sendEdit.

Examples
v4.8/Baileys runtime
On this page

This pattern works well for AI answers generated in stages: send an initial status, replace content when data is ready, then add follow-up suggestions.

1. Initial message

import { AIRich, Enums } from './lib/MessageBuilder.js';

const rich = new AIRich(sock, { dynamic: false })
  .addText('Preparing an answer…', { id: 'answer' })
  .addImage('https://example.com/placeholder.png', {
    id: 'image',
    status: Enums.IMAGINE_STATUS.GENERATING,
    update_text: 'Generating illustration',
  })
  .addMetadata('0 seconds', { id: 'duration' });

const sent = await rich.send(jid);
const messageId = sent.key.id;

dynamic: false keeps responseId stable across edits. With dynamic: true, IDs are regenerated on every build().

2. Replace blocks

rich
  .addText(
    'Here are the three main points from the article:\n\n' +
    '1. Rich responses carry sections and submessages.\n' +
    '2. Every block can be assigned an ID.\n' +
    '3. Edits use protocolMessage type 14.',
    { replace: 'answer' }
  )
  .addImage('https://example.com/chart.png', {
    replace: 'image',
    status: Enums.IMAGINE_STATUS.READY,
    width: 640,
    height: 360,
  })
  .addMetadata('Finished in 1.8 seconds', { replace: 'duration' });

await rich.sendEdit(jid, messageId);

replace finds a node by ID. The new node takes the old node’s position. You do not need to delete and re-add it.

3. Add suggestions

rich
  .addSuggest(['Summarize in three sentences', 'Translate to English'], {
    id: 'suggestions',
  })
  .addFooterAction(
    [
      { text: 'Open article', type: 'OPEN_FULL_VIEW', url: 'https://example.com/article' },
    ],
    { id: 'action' }
  );

await rich.sendEdit(jid, messageId);

4. Handle failures

try {
  await rich.send(jid);
} catch (err) {
  if (err.code === 'DUPLICATE_ID') {
    console.error('ID already in use:', err.message);
  } else {
    console.error('Failed to send rich response:', err);
  }
}

Wrap sendEdit() in its own try/catch. If the edit fails, the initial message remains and you can retry without sending a new one.

Notes

  • Use stable, descriptive IDs. Avoid timestamp-based IDs that change on every render.
  • Never combine replace and insertAt in one call.
  • send() already performs the initial sync. Call sendEdit() only for later changes.
  • unifiedResponse is base64-encoded; large payloads increase message size. Limit block count and code length.
  • unsupportedTypeAlert: true adds fallbacks for unsupported types. Disable it only when you know the target supports them.

Flow summary

send()  → initial message + sync edit
replace → swap content by id
sendEdit → send protocolMessage type 14