**COMING SOON — BETA RELEASE NOT FOR PRODUCTION USE**

The Headless SDK is currently under active development and is not yet available for beta use. We’ve published this documentation early to help you familiarize yourself with the SDK and prepare for the upcoming beta release.

Once the Headless SDK is available for beta, we’ll notify brands through our release notes.

When released as beta:

  • The beta release is intended for early evaluation purposes only and is not recommended for production use.
  • The SDK may contain bugs, errors, or incomplete or inaccurate information.
  • APIs, features, and behavior are subject to change as we continue to develop and improve the SDK.
  • Do not deploy or rely on the beta release in production environments.

Please note: The documentation, APIs, features, and examples may change before and during the beta period.

Lets an agent attach a free-text note to a conversation, visible to any agent who has (or later takes) the conversation. The SDK's wire contract is a single opaque string per conversation — it does not model note authorship, timestamps, or multiple entries. If your UI needs multiple, timestamped, attributed notes (one per agent-turn, not one shared field), you encode that structure into the string yourself, as shown below.

Clients: all examples use only the public MessagingClient API — client.on({ callback }) for events and client.dispatch({ action, value }) for actions. dispatch() returns a Promise; inline references below omit await for brevity, but always await it in real code.

Reading the current note

What it does: Surfaces a conversation's note with no separate fetch — it rides along with the conversation data you're already consuming.

Instructions:

client.on({
  callback: (event) => {
    if (event.type === 'conversations/update') {
      const { conversation } = event.value;
      console.log(conversation.note); // '' if no note has ever been set
    }
  },
});

Conversation.note is populated from the wire on every conversations/update notification — the same mapping used for every other conversation field. A note set by any agent, including an echo of the current agent's own write, surfaces automatically this way.

Writing a note

What it does: Overwrites the conversation's note with the given string.

Instructions:

await client.dispatch({
  action: 'conversation/set-summary-note',
  value: { conversationId, note: 'Consumer requesting a refund, escalate if unresolved by EOD.' },
});
  • Resolves void once UMS accepts the write; the updated value then arrives back through the conversations/update stream (see above) rather than in the dispatch() resolution itself.
  • This is a full overwrite, not an append — the previous note string is replaced entirely. If you need to preserve prior content, read the current conversation.note first and fold it into the string you send (see the multi-note pattern below).

⚠️ Note: dispatch() re-sends whatever string you give it as-is; the SDK does not parse, validate, or interpret its contents.

Pattern: multiple timestamped, attributed notes

Since the wire field is a single string, an agent workspace UI that wants one entry per agent-turn — author, timestamp, text — treats the string as an opaque JSON-encoded array it owns the shape of. The SDK has no awareness this string happens to contain JSON.

interface AgentNote {
  agentId: string;
  name: string;
  noteContent: string;
  noteId: string;
  time: number;
}

function parseNotes(note: string | undefined): AgentNote[] {
  if (!note) return [];
  try {
    const parsed = JSON.parse(note);
    return Array.isArray(parsed) ? parsed : [];
  } catch {
    return [];
  }
}

async function saveNote(conversationId: string, existingNote: string | undefined, agent: { id: string; name: string }, content: string) {
  const notes = parseNotes(existingNote);
  const lastNote = [...notes].sort((a, b) => b.time - a.time)[0];

  if (lastNote?.agentId === agent.id) {
    // Same agent revisiting their own last note: update it in place rather than appending.
    const idx = notes.findIndex((n) => n.noteId === lastNote.noteId);
    notes[idx] = { ...notes[idx], noteContent: content };
  } else {
    notes.push({
      agentId: agent.id,
      name: agent.name,
      noteContent: content,
      noteId: `${agent.id}::${Date.now()}`,
      time: Date.now(),
    });
  }

  await client.dispatch({
    action: 'conversation/set-summary-note',
    value: { conversationId, note: JSON.stringify(notes) },
  });
}
  • The whole array is re-serialized and re-sent on every save — there's no per-note server-side patch. Always read conversation.note fresh (not a stale local copy) immediately before building the array to save, to minimize the window for the next caveat.

⚠️ Note: there's no concurrency control. If two agents save around the same time, the second set-summary-note dispatch wins and silently overwrites the first, since the entire array is replaced on every write. Acceptable for a lightweight notes UI; an integration needing stronger guarantees would need its own optimistic-concurrency layer (e.g. compare-and-swap on a version field inside the JSON) on top of this.

⚠️ Note: if an automated conversation summary feature is enabled for the account, it is expected to append an entry to this same array with an additional isAutoSummary: true key. Nothing in the SDK or the pattern above special-cases that key — such an entry renders like any other note unless your UI explicitly checks for it.

Enforcing the max note length

What it does: Site setting messaging.agent.notes.max.length caps how long a note string may be; UMS does not reject over-length writes, so validate client-side if you want to warn the agent before they lose content on submit.

Instructions:

const { maxAgentNotesLength } = session.permissions.configuration;

if (JSON.stringify(notes).length > maxAgentNotesLength) {
  // warn the agent / truncate before dispatching
}

maxAgentNotesLength is read once at connect time as part of session.permissions.configuration (see authentication-flow.md for the full SessionObject / permissions shape). If you adopt the multi-note JSON pattern above, remember the length limit applies to the serialized array as a whole, not to each individual note's text.