---
title: "Create or update a note"
method: POST
path: "/api/notes"
tags: ["Notes"]
---

# Create or update a note

`POST /api/notes`

## Request

- Base URL: `https://api.engram.page`
- URL: `https://api.engram.page/api/notes`
- Auth: HTTP bearer

Creates a note or updates the existing one at the same path. Updates are merged convergently (CRDT) with the stored content. Optionally pass `base_hash` — the `content_hash` you last read — for compare-and-swap semantics: if the note changed since that read, the write returns 409 with the current server note instead of merging. Notes over 10MB are rejected with 413, and exceeding the plan's note cap returns 402.

## Request body

- UpsertNoteRequest
  - `base_hash` string, nullable: Compare-and-swap guard: the content_hash you last read for this note. If the note changed since, the write returns 409 instead of merging.
  - `content` string
  - `folder` string, nullable
  - `mtime` number, float, required
  - `path` string, required
  - `tags` string[]
  - `title` string, nullable
  - `version` integer, nullable

## Response `201`

Created/updated

- NoteResponse
  - `note` Note, required
    - `content` string
    - `content_hash` string, nullable
    - `description` string, nullable: OKF frontmatter `description` field
    - `fm_created` string, date-time, nullable: OKF frontmatter `created`/`date` field
    - `fm_timestamp` string, date-time, nullable: OKF frontmatter `timestamp`/`modified`/`updated` field
    - `folder` string, nullable
    - `id` string, uuid, nullable
    - `links` NoteLink[]: Outgoing link/embed edges, resolved. Wikilink and markdown syntax alike.
      - `alias` string, nullable: Display text: `[[target|alias]]`'s alias, or `[label](target)`'s label.
      - `anchor` string, nullable: Heading/block reference: `[[target#anchor]]` or `[x](target#anchor)`.
      - `dangling` boolean, required: True when the target could not be resolved.
      - `link_type` string, required: "wikilink" or "embed" — whether the link EMBEDS its target. Not the source syntax; markdown-form links carry these same two values.
      - `target_attachment_id` string, uuid, nullable
      - `target_note_id` string, uuid, nullable
      - `target_path` string, nullable: Resolved note path.
      - `target_text` string, required: The link target, percent-DECODED. `[[Target]]` and `[x](Target.md)` both yield `Target`/`Target.md` here — not the literal source bytes.
    - `mtime` number, float: Client mtime (epoch seconds)
    - `parse_reason` object, nullable: Present when parse_status is "degraded": {code, message, detail}
    - `parse_status` 'ok' | 'degraded', nullable: Frontmatter parse outcome
    - `path` string, required
    - `resource` string, nullable: OKF frontmatter `resource` field
    - `tags` string[]
    - `title` string, nullable
    - `type` string, nullable: OKF frontmatter `type` field
    - `updated_at` string, date-time, nullable
    - `version` integer, nullable

## Other responses

- `409`: Version conflict
- `413`: Note exceeds 10MB
- `422`: Validation error

---

[API](https://skmtc.dev/engram/apis/engram-api.md) · [All operations](https://skmtc.dev/engram/apis/engram-api/llms.txt) · [OpenAPI document](https://skmtc.dev/engram/apis/engram-api/revisions/8c4c2eec29dc?raw)
