Formatting
How your markdown reaches Zoom — the tokens that render, the ones Zoom cannot carry, and the structured fallback.
Zoom Team Chat accepts two kinds of message body: structured segments (plain text
plus a per-segment bold or italic flag), and Markdown, which you turn on per
message with is_markdown_support: true.
The adapter uses Markdown by default, because it is the only mode that can render
links, strikethrough, block quotes, and images. Markdown mode also makes Zoom
ignore the structured style field, so everything you want to see has to live in
the text itself.
The same message, three ways
You write (Chat SDK markdown)
**Deploy finished** Shipped `api-gateway` to _production_ in 94s.
Zoom receives
*Deploy finished* Shipped `api-gateway` to _production_ in 94s.
Zoom shows
Deploy finished
Shipped api-gateway to production in 94s.
What reaches Zoom
| What you write | What the adapter sends | What Zoom renders |
|---|---|---|
**bold** |
*bold* |
Bold |
_italic_ |
_italic_ |
Italic |
`code` |
`code` |
Monospace |
~strikethrough~ |
~strikethrough~ |
|
> quote |
> quote |
Indented, italic (Zoom’s quote style) |
[label](https://example.com) |
label <https://example.com> |
Label plus a clickable URL |
 |
<img:https://example.com/a.png|alt> |
The image, if its host is trusted |
# Heading |
*Heading* |
A bold line — Zoom has no heading token |
- item or * item |
• item |
A bullet line |
__underline__ |
underline |
Plain text — Zoom has no underline token |
| A table | Pipe-separated rows, bold header | Plain text rows — Zoom has no table token |
Code spans and fenced blocks are protected while the rest is converted, so code
that merely looks like ~strike~ or __underline__ is left exactly as written.
An empty message is never sent, because the Zoom API rejects empty content.
Bold is a single asterisk
This is the one place where Zoom’s documentation contradicts itself. Its chatbot
Markdown page shows **bold** in the syntax table but *bold* in the request
example. The live client follows the example:
| Token | Result |
|---|---|
*bold* |
bold |
***bold*** |
bold |
_italic_ |
italic |
~strike~ |
|
**bold** |
plain text |
__underline__ |
plain text |
So **bold** in your Chat SDK markdown arrives at Zoom as *bold*. If you write
the Zoom token directly, remember that *text* means emphasis in standard
Markdown, so the adapter will read it as italic. Write standard Markdown and let
the converter translate.
Links
A markdown link keeps its label and makes the URL clickable:
See [the docs](https://example.com) for more
arrives as See the docs <https://example.com> for more. Zoom documents the bare
<url> form; the labelled form also works in current clients.
Images need a trusted host
The image token is sent correctly, but the Zoom client only renders images from
domains it trusts. Images from other hosts are dropped client-side, with a
[Security] Blocked image from untrusted domain warning in the browser console.
Add the host to your app’s trusted-domain list, or use one Zoom already trusts,
such as developers.zoom.us.
This is a Zoom client rule. The adapter cannot override it.
Structured fallback
Set markdownSupport: false (or ZOOM_MARKDOWN_SUPPORT=false) to send structured
segments instead:
createZoomAdapter({ markdownSupport: false });
That mode bolds a paragraph only when the whole paragraph is bold, including a whole bold heading. Inline bold and italic are flattened, and links, strikethrough, block quotes, and images keep their text but lose their formatting. See Configuration for the option and its environment variable.
Reading an incoming message
Incoming text is parsed with Zoom’s own conventions:
| Token | Meaning |
|---|---|
__text__ |
Underline, not bold |
~text~ |
Strikethrough with a single tilde |
Everything else — **bold**, _italic_, `code`, # headings, * lists —
is standard. message.text holds the raw text the person typed, and
message.formatted holds the parsed tree.