> For the complete documentation index, see [llms.txt](https://docs.monolithforensics.com/monolith/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.monolithforensics.com/monolith/monolith-api/notes-api.md).

# Notes API (Coming Soon)

{% hint style="warning" %}
The Notes API is currently under development and is scheduled for an upcoming release.  These docs are posted for users to get a preview of the API.
{% endhint %}

The Notes API lets you programmatically create, read, update, delete, organize, link, and export case notes in Monolith. Notes are rich-text (HTML) documents attached to a case, optionally organized into folders and linked to other Monolith objects such as evidence items, tasks, and timeline events.

Each endpoint has its own page with full request/response examples and Python samples:

<table data-search="false"><thead><tr><th>Endpoint</th><th>Method &#x26; Path</th><th>Docs</th></tr></thead><tbody><tr><td>List / get notes</td><td><code>GET /v1/notes</code>, <code>GET /v1/notes/{uuid}</code></td><td><a href="/pages/819c16b9373ece422055692502ff89f1f0a8f9cd">Get Notes</a></td></tr><tr><td>Create a note</td><td><code>POST /v1/notes</code></td><td><a href="/pages/14d5f3ea223a91fe77b15e0444f741bd93bd4c24">Create Note</a></td></tr><tr><td>Update a note</td><td><code>PATCH /v1/notes/{uuid}</code></td><td><a href="/pages/677e2a516d0ea19808d9f8d6381548491904adb4">Update Note</a></td></tr><tr><td>Append to a note</td><td><code>PATCH /v1/notes/{uuid}/append</code></td><td><a href="/pages/3171297ac62e8ced7af06e2107c3eb6d81f3827c">Append Note</a></td></tr><tr><td>Delete a note</td><td><code>DELETE /v1/notes/{uuid}</code></td><td><a href="/pages/5f7cecbd15cef441476587b45c7026c93af2fdcb">Delete Note</a></td></tr><tr><td>Note version history</td><td><code>GET /v1/notes/{uuid}/versions</code></td><td><a href="/pages/a35979fc6ca6594f89ca7dbb0a068814aa30de05">Get Note Versions</a></td></tr><tr><td>Note authors</td><td><code>GET /v1/notes/note-users</code></td><td><a href="/pages/21d90d617cdd86368a08b9a4514b8dbb53656ffb">Get Note Users</a></td></tr><tr><td>Export notes (PDF/DOCX)</td><td><code>POST /v1/notes/export</code></td><td><a href="/pages/27357aab827952a8dcf7217525ad10629458f5e9">Export Notes</a></td></tr><tr><td>Note Images</td><td><code>POST /v1/notes/{uuid}/images</code> and related</td><td><a href="/pages/fc24fa480a8934b085516f28e40a3ad5c80ed647">Note Images</a></td></tr></tbody></table>

## Base URL

All public API endpoints are served under the `/v1` prefix on your Monolith instance:

```
https://<your-monolith-host>/v1/notes
```

Replace `<your-monolith-host>` with the hostname of your Monolith deployment.

## Authentication

Every request must include a Monolith API key in the `X-Api-Key` header. API keys are created in the Monolith app by an administrator and are tied to an API user; all activity performed with the key (note authorship, exports, etc.) is attributed to that user and logged.

```
X-Api-Key: <your-api-key>
```

Requests without a valid key receive `401 Unauthorized`.

### Quick check

Verify your key and connectivity with the `/v1/info` endpoint:

```python
import requests

BASE_URL = "https://your-monolith-host/v1"
API_KEY = "your-api-key"

response = requests.get(
    f"{BASE_URL}/info",
    headers={"X-Api-Key": API_KEY},
)
print(response.json())
# {"message": "Monolith API is running", "success": true, "user": {...}}
```

### Reusable session

The Python examples throughout these docs assume a `requests.Session` configured like this:

```python
import requests

BASE_URL = "https://your-monolith-host/v1"

session = requests.Session()
session.headers.update({"X-Api-Key": "your-api-key"})
```

## Conventions

### Identifiers

Notes are addressed by their `uuid` (a string UUID). Numeric ids (`case_note_id`, `case_id`, `user_id`) appear in responses but endpoints take UUIDs unless documented otherwise.

### Note content

* `note_tag` — the note's title.
* `note_data` — the note body as HTML. On create/update the server normalizes the HTML through the rich-text renderer, so what you read back may be a cleaned-up version of what you sent. **Unsupported markup is silently removed, not rejected** — see [Constructing `note_data` safely](#constructing-note_data-safely) before building note bodies programmatically.
* `is_folder` — folder notes group other notes. A note's `parent_id` must reference a folder note in the same case.

### Pagination

List endpoints accept `page` (default `1`) and `page_size` (default and maximum `3000`) and return:

```json
{
  "data": [ ... ],
  "page": 1,
  "next_page": 2,
  "page_size": 3000,
  "total": 4512
}
```

`next_page` is `null` on the last page.

```python
def get_all_notes(session, **params):
    """Iterate every page of a notes query."""
    page = 1
    while page is not None:
        response = session.get(f"{BASE_URL}/notes", params={**params, "page": page})
        response.raise_for_status()
        body = response.json()
        yield from body["data"]
        page = body["next_page"]
```

### Timestamps

Timestamps (`created_on`, `updated_on`) are returned in full ISO-8601 UTC format — `YYYY-MM-DDTHH:mm:ss.sssZ`, e.g. `2026-08-03T14:21:09.000Z`. `updated_on` is `null` until a note is first updated. The export endpoint renders timestamps into the document in a timezone/format of your choice — see [Export Notes](/monolith/monolith-api/notes-api/export-notes.md).

### Errors

Validation failures return `400` with a `message` and a Zod `errors` array describing each invalid field:

```json
{
  "message": "Invalid request body",
  "errors": [
    {
      "code": "invalid_type",
      "expected": "string",
      "received": "undefined",
      "path": ["case_uuid"],
      "message": "case_uuid is required"
    }
  ]
}
```

Service-level errors return a `message` only:

| Status | Meaning                                                                    |
| ------ | -------------------------------------------------------------------------- |
| `400`  | Invalid input (bad parent folder, wrong case, unsupported image type, ...) |
| `401`  | Missing or invalid API key                                                 |
| `404`  | Note, case, or link not found                                              |
| `500`  | Unexpected server error (details are logged server-side, not returned)     |

```json
{ "message": "Note not found" }
```

## Constructing `note_data` safely

Every `note_data` value you send — on create, update, or append — is parsed into the Monolith rich-text editor's document model and re-serialized back to HTML before it is stored. This guarantees that whatever the API stores can be opened and edited in the Monolith app.

The important consequence: **this is a normalization step, not a validator that rejects bad input.** Markup the editor's schema doesn't understand is silently dropped or unwrapped — the request still succeeds. If you send unsupported elements, you lose them without an error. The response to every create/update/append call contains the note as it was actually stored, so you can always compare what came back against what you sent.

### Note Data HTML Examples

Valid examples of HTML data that can be correctly passed to the API are provided at [Note Data Examples](/monolith/monolith-api/notes-api/note-data-examples.md).

### Supported block elements

| Element                 | HTML                                                                                   | Notes                                                                                                                                                                                                                                                                                                                                                                                                             |
| ----------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Paragraph               | `<p>`                                                                                  | Bare text is wrapped in a paragraph automatically. Supports `style="text-align: left/center/right/justify"`.                                                                                                                                                                                                                                                                                                      |
| Headings                | `<h1>`–`<h6>`                                                                          | Supports the same `text-align` style.                                                                                                                                                                                                                                                                                                                                                                             |
| Bullet / numbered lists | `<ul>`, `<ol>`, `<li>`                                                                 | Nesting supported.                                                                                                                                                                                                                                                                                                                                                                                                |
| Task lists              | `<ul data-type="taskList">` with `<li data-type="taskItem" data-checked="true/false">` | Nesting supported.                                                                                                                                                                                                                                                                                                                                                                                                |
| Blockquote              | `<blockquote>`                                                                         |                                                                                                                                                                                                                                                                                                                                                                                                                   |
| Code block              | `<pre><code class="language-python">...</code></pre>`                                  | Language from the `language-*` class (defaults to `plaintext`). `data-wrap="true"` on the `<pre>` enables soft wrapping. Escape the code's HTML entities.                                                                                                                                                                                                                                                         |
| Horizontal rule         | `<hr>`                                                                                 |                                                                                                                                                                                                                                                                                                                                                                                                                   |
| Table                   | `<table>`, `<tr>`, `<th>`, `<td>`                                                      | `colspan`/`rowspan` supported.                                                                                                                                                                                                                                                                                                                                                                                    |
| Image                   | `<img>`                                                                                | Keeps `src`, `alt`, `title`, `width`, `height`, `data-uuid`. Sizing via a `style` attribute is stripped — use the `width`/`height` attributes. Images are inline and get wrapped in a paragraph. Prefer the `markup` returned by the [image upload endpoint](broken://pages/87af72a15eca4d670018febc486f28ff16ee0220#upload-an-image-to-a-note-recommended); base64 `data:` URIs are accepted but bloat the note. |
| Line break              | `<br>`                                                                                 |                                                                                                                                                                                                                                                                                                                                                                                                                   |

### Supported inline formatting (marks)

| Formatting    | HTML                                                                                                                                                                                                                                            |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bold          | `<strong>` or `<b>`                                                                                                                                                                                                                             |
| Italic        | `<em>` or `<i>`                                                                                                                                                                                                                                 |
| Underline     | `<u>`                                                                                                                                                                                                                                           |
| Strikethrough | `<s>`, `<del>`, or `<strike>`                                                                                                                                                                                                                   |
| Inline code   | `<code>`                                                                                                                                                                                                                                        |
| Link          | `<a href="https://...">` — standard protocols (`http`, `https`, `mailto`, `tel`, `ftp`) and relative paths survive; unsafe schemes (`javascript:`, `data:`) are removed. `target`, `rel`, and `class` are overwritten with the editor's values. |
| Text color    | `<span style="color: #b91c1c">` — the `color` style is honored **only on a `<span>`**; a `color` style on `<p>` or headings is stripped.                                                                                                        |
| Highlight     | `<mark data-color="#fef08a">`                                                                                                                                                                                                                   |

### What gets removed or rewritten

* **Unknown block containers** (`<div>`, `<section>`, `<article>`, `<span>` wrappers, ...) are unwrapped: their text content survives, the element and all of its attributes do not.
* **Elements with no schema equivalent and non-text content** — `<script>`, `<style>`, `<iframe>`, `<video>`, `<audio>`, `<form>`, `<canvas>`, embeds — are dropped entirely, including their content.
* **Attributes** not listed above are stripped: `id`, custom `class` values, event handlers (`onclick`, ...), and custom `data-*` attributes (except the documented ones like `data-uuid`, `data-checked`, `data-wrap`, `data-color`).
* **Inline styles** other than `color` (on spans) and `text-align` (on paragraphs/headings) are stripped — including `font-family`, `font-size`, `background`, margins, and widths. There is no font-family/font-size support; those belong in the export step ([Export Notes](/monolith/monolith-api/notes-api/export-notes.md) accepts a `font` for DOCX).
* **Links with unsafe schemes** (`javascript:`, `data:`) lose the link mark; the anchor text itself survives as plain text.
* **HTML comments** are removed.
* Insignificant whitespace between tags is collapsed per normal HTML parsing rules — don't rely on indentation or blank lines for layout; use paragraphs and `<br>`.

Equivalent markup is also normalized to canonical form (no content is lost): `<b>` → `<strong>`, `<i>` → `<em>`, `<del>`/`<strike>` → `<s>`, table cell content gets wrapped in paragraphs and tables gain `<colgroup>`/`<tbody>` scaffolding, and task items are re-rendered with the editor's checkbox markup. Expect the stored HTML to differ from your input even when nothing was dropped.

### Practical guidance

{% stepper %}
{% step %}

#### Stick to the tables above

Everything listed round-trips losslessly.
{% endstep %}

{% step %}

#### Escape interpolated text content

Use `html.escape()` in Python so user data can't be misread as markup — especially inside `<pre><code>` blocks.
{% endstep %}

{% step %}

#### Verify the round trip

When developing an integration, the create/update/append response contains the stored `note_data`.

```python
import html

payload = "<p>Report for <strong>EV-001</strong></p><pre><code class=\"language-text\">" \
          + html.escape("sha256: 9e10...<computed>") + "</code></pre>"

response = session.post(f"{BASE_URL}/notes", json={
    "case_uuid": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
    "note_tag": "Hash Report",
    "note_data": payload,
})
stored = response.json()["data"]["note_data"]

# The stored HTML won't be byte-identical (attribute order, editor classes),
# but the text content should survive intact:
import re
strip = lambda s: re.sub(r"<[^>]+>", "", s)
assert strip(stored).replace("&lt;", "<").replace("&gt;", ">") \
    == strip(payload).replace("&lt;", "<").replace("&gt;", ">"), "content was lost in normalization"
```

{% endstep %}

{% step %}

#### Avoid unsupported presentation

Don't embed presentation you can't express in the schema. If you need pixel-perfect layout (letterheads, complex styling), attach a rendered file to the case instead and keep the note to the substantive content.
{% endstep %}
{% endstepper %}

## A 60-second tour

{% stepper %}
{% step %}

#### Create a note in a case

```python
note = session.post(f"{BASE_URL}/notes", json={
    "case_uuid": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
    "note_tag": "Initial Triage",
    "note_data": "<p>Device received and photographed.</p>",
}).json()["data"]
```

{% endstep %}

{% step %}

#### Append a finding to it

```python
session.patch(f"{BASE_URL}/notes/{note['uuid']}/append", json={
    "note_data": "<p>Imaging completed at 14:32 UTC.</p>",
})
```

{% endstep %}

{% step %}

#### Link it to an evidence item

```python
session.post(f"{BASE_URL}/notes/object-links", json={
    "note_uuid": note["uuid"],
    "object_id": "e7c40f6e-2f19-4b52-9d7a-52a5a24c3f10",
    "object_type": "evidence",
})
```

{% endstep %}

{% step %}

#### Export it to PDF and download

```python
export = session.post(f"{BASE_URL}/notes/export", json={
    "uuid": note["uuid"],
    "format": "pdf",
}).json()

pdf = requests.get(export["signedUrl"])
with open(export["filename"], "wb") as f:
    f.write(pdf.content)
```

{% endstep %}
{% endstepper %}
