Richtext

This page describes rich text formatting for the Entities API (for example, fields.description on a feature). The Entities API enforces a strict HTML whitelist: any unsupported tag results in a 400 response.

When sending rich text to the Entities API:

  • Always wrap content in a tag (e.g. <p>text</p>). Plain text is not accepted.
  • Use only the tags listed in the tables below.
  • Close every tag, including void elements (<br/>, <hr/>).
  • Quote all attribute values.
  • Use numeric character references such as &#160; rather than named ones such as &nbsp;.
📘

Notes API uses different rules

Feedback (notes) can arrive from many sources and is read-only in the UI, so the Notes API strips visual styling and keeps only semantic tags rather than rejecting input.

TagDescription
<h1>, <h2>Headers, these two levels only
<p>Paragraph
<b>Bold
<i>Italics
<u>Underline
<code>Code
<ul>Unordered list
<ol>Ordered list
<li>List item
<a>Link
<hr/>Horizontal line
<pre>Code block
<blockquote>Block quote
<s>Strikethrough
<br/>Line break
<img>Image, inside a <p>

Tables

Give every table a header row. Cells hold text and the inline tags above; a block element inside a cell and a merged cell (colspan, rowspan) are rejected. A table without a header row is accepted, but it is stored with an empty header row, so the first line of the table comes back blank.

TagDescription
<table>Table
<thead>, <tbody>Table sections
<tr>Table row
<th>Header cell
<td>Body cell
<table>
  <thead>
    <tr><th>Field</th><th>Type</th><th>Required</th></tr>
  </thead>
  <tbody>
    <tr><td>name</td><td>string</td><td>yes</td></tr>
  </tbody>
</table>

Checklists

A checklist is an unordered list whose items carry a checkbox. Only <input type="checkbox"/> is required, and checked="checked" is what stores the ticked state. The classes and the read-only attributes below are what a read returns; they are accepted but not required on the way in.

<ul>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" readonly="readonly" />&#160;open task</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" checked="checked" disabled="disabled" readonly="readonly" />&#160;done task</li>
</ul>

Code blocks with a language

A code block can name its language, which is what keeps syntax highlighting after a round trip. A block with no language stays a bare <pre>.

<pre><code class="language-ruby">Widget.where(active: true)</code></pre>

Placeholder links

Some content has no HTML equivalent: mentions of a person or an item, emoji, embeds such as Figma or Loom, and collapsible sections. Reading a description returns each of them as a link whose destination starts with urn:pb:node:.

<p>Owner: <a href="urn:pb:node:mention:6c169e828fab63509a9e884f">Anchor: @Jane Doe</a></p>

Those links are how the content is put back when you write the description again.

  • Send a link back exactly as you received it and the original content is restored in its place.
  • Delete the link and you delete the content it stands for.
  • Duplicate the link and you duplicate that content.
  • The link text is a label for people reading the HTML and is ignored, so changing it is harmless. Changing the destination is not: a urn:pb:node: destination that matches nothing is dropped along with the link.