Skip to main content

Sender Profiles

Apps can control the name and avatar a chat message appears with. The optional sender object on message-sending endpoints covers two independent concepts:

{
"sender": {
"id": "support-bot",
"name": "Support (EMEA)",
"imageUrl": "https://example.com/support-emea.png"
}
}
  • sender.name / sender.imageUrl — per-message display overrides. The supplied name and image are stored on that one message and rendered in place of the author's profile. They are display data, not an identity: they never create or modify anything beyond the message itself.
  • sender.id — a configured bot persona. If the id matches a persona you configured for your app in Developer settings, the message is authored by that persona's stable, @-mentionable identity. Any other value is accepted and ignored.

You can use either mechanism on its own or combine them.

Per-message display overrides

Pass sender.name and/or sender.imageUrl on /chat.post, /chat.postEphemeral, or /chat.startStream to control how that single message is displayed:

{
"groupId": "88bebce7-6cbb-4666-96f9-5c02d73e6661",
"text": "Build **#4021** passed",
"sender": { "name": "CI · frontend", "imageUrl": "https://example.com/ci.png" }
}

Rules:

  • name is capped at 128 characters (UTF-16 code units). Longer values return 400.
  • imageUrl must be an absolute HTTP(S) URL with a host. Anything else returns 400.
  • Both fields are optional and independent. An omitted field falls back to the author's profile (the app's configured display, or the selected persona's) at render time.
  • Overrides are immutable: they are snapshotted onto the message at send time. Renaming your app or reconfiguring a persona later never retroactively changes past messages, and sending with an override never renames your app or a persona.

Display overrides are presentation, not authentication. Clients always retain the real author (bot badge and via-app attribution), and API consumers should rely on the message's userId / userType — never the display name — for attribution and bot-loop prevention. See Identity & Principals.

Configured bot personas

A persona is a stable secondary identity for your app — for example one app posting as support-bot, onboarding-bot, and sales-bot. Unlike a display override, a persona is a real address in the workspace: users can @-mention it, and its profile (name and avatar) is managed centrally in your app's configuration.

Configuring personas

Personas are configured on the app, not through the message API:

  1. Open Roam Administration → Developer and edit your app (API key or OAuth app).
  2. In the bot configuration section, click Add Bot Persona.
  3. Give the persona a code (its sender.id), a display name, and optionally an avatar image.

Constraints:

  • Up to 10 personas per app.
  • Codes are 1–16 characters, unique per app (case-insensitive), and stored lowercase. The code _ is reserved for the app's root identity.
  • Display names are 1–128 characters.

Persona addresses are provisioned when you save the configuration (for OAuth apps, across existing installs shortly after saving). Removing a persona stops it from being selectable for new messages; messages it already sent keep rendering with its identity.

Sending as a persona

Pass the persona's code as sender.id:

{
"groupId": "88bebce7-6cbb-4666-96f9-5c02d73e6661",
"text": "A new ticket was assigned to you.",
"sender": { "id": "support-bot" }
}
  • Matching is trimmed and case-insensitive.
  • Selection is lookup-only: sending never creates or updates persona addresses. Provisioning happens only through configuration.
  • An omitted, empty, or _ id posts as the app's root identity.
  • You can combine sender.id with name/imageUrl: the message is authored by the persona, with the supplied display override applied to that message only. The persona's configured profile is unchanged.

Unknown ids are accepted and ignored

A sender.id that does not match a configured persona — including malformed or over-length values — is not an error. The request succeeds, the message is authored by the app's root identity, and any supplied name/imageUrl is still applied as a per-message display override.

This keeps integrations that put dynamic data in sender.id (dates, ticket ids, phone numbers) working, without minting a new bot identity per value. If you want messages attributed to a distinct, mentionable identity, configure it as a persona; merely supplying an id never creates one.

Authorization is always the app root

Personas affect authorship and display only. Chat and group access, attachment ownership, and chat.update / chat.delete ownership always resolve through your app's root identity — the same one used when you send without a sender. Adding your app to a channel is sufficient for all of its personas to post there; personas cannot be granted or denied access individually, and a persona cannot be added to a channel as a member.

Endpoint support

Endpointsender.idsender.name / imageUrl
/chat.postSelects a configured personaPer-message display override
/chat.postEphemeralSelects a configured personaPer-message display override
/chat.startStreamSelects a configured persona (fixed for the stream's lifetime)Per-message display override
/chat.typingSelects a configured personaRejected (400) — typing shows the persona's configured profile
/chat.update, /chat.deleteRejected (400) — sender is derived from the original messageRejected (400)
/v0/chat.sendMessage (deprecated)Same semantics as /chat.postSame semantics as /chat.post

Personal access tokens do not support sender. A PAT always posts as the personal bot provisioned with the token; any sender field on a PAT request returns 400 (access_mode_not_supported). OAuth apps installed with personal access do support sender, with personas configured on the app.

Reading sender display from the API

Messages sent with a display override carry it in read payloads, so mirrors and bridges can render the same name/avatar users saw:

The field is additive: userId (v1) remains the authoring identity — the app's root or a configured persona — and resolves through user.info as usual.

{
"userId": "b7c34e90-2f1a-4d8e-9a6b-1c2d3e4f5a6b",
"userType": "bot",
"text": "Build **#4021** passed",
"sender": { "name": "CI · frontend", "imageUrl": "https://example.com/ci.png" },
"timestamp": 1718900000000000
}

Behavior change (August 2026)

Previously, sending with an arbitrary sender.id created a new bot address for that id on the fly, and re-sending could rename it. This minted junk identities (dates, ticket numbers, phone numbers as ids) and is no longer the case:

BeforeNow
Unknown sender.id created a new bot address on sendAccepted and ignored; message is authored by the app root
sender.name / imageUrl renamed the sender's stored profileApplied as an immutable per-message display override
Configured persona ids selected the personaUnchanged — still selects the persona (lookup-only)
PAT with any sender → 400Unchanged

Requests that relied on minting a durable, mentionable identity from an arbitrary id still return 200, but the message is authored by the app root with the supplied display applied per-message. To keep a distinct stable identity, configure it as a persona. Identities created under the old behavior remain valid for rendering historical messages, but can no longer be selected for new sends unless configured.