v7.6.0

NoteStream API

Base URL: https://api.notestream.cloud

All paths below are relative to this base URL. For example, POST /search means POST https://api.notestream.cloud/search.

There is no separate AI namespace — agents call the same endpoints as the app. Every endpoint accepts either a user API token or a session JWT:

X-API-Key: your-api-key-here
Authorization: Bearer <jwt>

API keys can be created from Settings > API Keys in the NoteStream app. A token maps to a real user, so a request sees exactly what that user can see (their own notes plus everything shared with them via direct shares, hashtags, groups, and share links).

A note and its content are separate entities with a 1:1 mapping. A note holds metadata (ID, author, sharing, timestamps); note content holds the document body as ProseMirror JSON. Every note has exactly one content record, and they share the same ID. The root node is always { type: "doc", content: [...] }.


Search notes

Search across every note the caller can see. The request body is a filter AST; the only required field is visibility.

POST /search

Request body

{
  "ast": {
    "visibility": "my-world",
    "text": "meeting"
  },
  "page": 1,
  "pageSize": 25
}
Field Type Required Description
ast object Yes Filter AST (see below).
page number No Page number (starts at 1). Defaults to 1.
pageSize number No Results per page. Defaults to 25, max 100.
shareCodes string[] No Share-link codes the caller has opened, to include link-shared notes. Max 20.

Filter AST fields:

Field Type Required Description
visibility string Yes One of "public", "shared-with-me", "all", "my-world". "my-world" = the caller's own notes plus notes shared with them.
text string No Case-insensitive substring match on note text.
author object No { "op": "me" }, or { "op": "is", "ids": ["<email-or-userId>"] }.
hashtags array No [{ "hashtag": "roadmap" }].
mentions array No [{ "handle": "john" }].
sortBy string No "newest", "oldest", "streamOrder", "lastUpdated", "relevance".

Response

{
  "notes": [
    {
      "id": "0192d4e5-7a8b-7c6d-9e0f-1a2b3c4d5e6f",
      "author": {
        "id": "user-uuid",
        "firstName": "Ada",
        "lastName": "Lovelace",
        "email": "ada@example.com",
        "profilePicture": null
      }
    }
  ],
  "totalCount": 250,
  "page": 1,
  "pageSize": 25
}

Results are note IDs (with author info), not snippets — fetch a note's body with GET /content/:id.

Errors

Status Description
401 Missing or invalid credentials
422 Invalid request body / filter AST

Get a note

Retrieve the metadata of a note the caller owns.

GET /notes/:id

Parameters

Name In Type Description
id path string The note ID (UUID)

Response

{
  "id": "0192d4e5-7a8b-7c6d-9e0f-1a2b3c4d5e6f",
  "authorId": "user-uuid",
  "isShared": false,
  "pinnedPosition": null,
  "folderId": null,
  "deletedAt": null,
  "createdAt": 1711900800000,
  "updatedAt": 1711900800000
}

Errors

Status Description
401 Missing or invalid credentials
404 Note not found or not owned by the caller

Create a note

Create a new note from ProseMirror JSON content. The note is assigned to the caller.

POST /notes

Request body

{
  "content": {
    "type": "doc",
    "content": [
      {
        "type": "paragraph",
        "content": [{ "type": "text", "text": "A brand new note" }]
      }
    ]
  },
  "isShared": true,
  "anonymousRole": "VIEWER"
}
Field Type Required Description
content object Yes ProseMirror document with type: "doc".
isShared boolean No Share the note with anonymous link access on creation. Defaults to false.
anonymousRole string No "VIEWER" or "EDITOR". Role for anyone with the link. Defaults to "VIEWER".
groupId string No Create the note directly inside this group. Requires CONTRIBUTOR or higher membership in the group (else 403).
pinnedPosition string No Fractional-index position to pin the note. Omit to leave unpinned.

Response

Returns the full note object (same shape as GET /notes/:id).

Errors

Status Description
401 Missing or invalid credentials
403 groupId given but you lack posting permission in that group
422 Invalid ProseMirror JSON structure

Read note content

Retrieve a note's content as ProseMirror JSON.

GET /content/:id

Parameters

Name In Type Description
id path string The note ID (UUID)

Response

{
  "content": {
    "type": "doc",
    "content": [
      {
        "type": "paragraph",
        "content": [{ "type": "text", "text": "Hello world" }]
      }
    ]
  }
}

If the note has no content, returns an empty document:

{ "content": { "type": "doc", "content": [{ "type": "paragraph" }] } }

Errors

Status Description
401 Missing or invalid credentials
404 Note not found or not owned by the caller

Replace note content

Replace the entire content of an existing note with new ProseMirror JSON.

PUT /content/:id

Parameters

Name In Type Description
id path string The note ID (UUID)

Request body

{
  "content": {
    "type": "doc",
    "content": [
      {
        "type": "paragraph",
        "content": [{ "type": "text", "text": "Updated content" }]
      }
    ]
  }
}

The content field must be a valid ProseMirror document with type: "doc" and a content array.

Response

{ "success": true }

Errors

Status Description
401 Missing or invalid credentials
404 Note not found or not owned by the caller
422 Invalid ProseMirror JSON structure

Update note sharing

Set a note's sharing entries. Supports anonymous (anyone-with-link) and per-email sharing.

PUT /notes/sharing/:id

Parameters

Name In Type Description
id path string The note ID (UUID)

Request body

{
  "entries": [
    { "principalType": "ANONYMOUS", "principal": "anonymous", "role": "VIEWER" }
  ]
}
Field Type Required Description
entries array Yes The full set of sharing entries for the note.
entries[].principalType string Yes "ANONYMOUS" (anyone with the link) or "EMAIL".
entries[].principal string Yes "anonymous" for ANONYMOUS, otherwise the member email.
entries[].role string Yes "VIEWER" or "EDITOR".
isPublishedToGlobalStream boolean No Publish the note to the public global stream.

Errors

Status Description
401 Missing or invalid credentials
404 Note not found or not owned by the caller
422 Invalid request body

Note: When a note is shared anonymously it becomes accessible via a shareable link. The URL removes dashes from the note ID: https://notestream.cloud/shared/note/{noteId without dashes}. For example, note 0192d4e5-7a8b-7c6d-9e0f-1a2b3c4d5e6f becomes https://notestream.cloud/shared/note/0192d4e57a8b7c6d9e0f1a2b3c4d5e6f.


Create a group

Create a new group. The caller becomes its OWNER member.

POST /groups

Request body

{
  "name": "Design Team",
  "description": "Where the design crew collaborates"
}
Field Type Required Description
name string Yes Group name (1–120 chars).
description string No Optional description (max 2000 chars).

Response

Returns the created group:

{
  "group": {
    "id": "0192d4e5-7a8b-7c6d-9e0f-1a2b3c4d5e6f",
    "slug": "design-team-a1b2c3d4",
    "name": "Design Team",
    "description": "Where the design crew collaborates",
    "logo": null,
    "ownerId": "user-uuid",
    "shareCode": null,
    "shareCanEdit": false,
    "createdAt": "2026-06-27T16:00:00.000Z",
    "updatedAt": "2026-06-27T16:00:00.000Z"
  }
}

Errors

Status Description
401 Missing or invalid credentials
422 Invalid request body

List groups

List the groups the caller belongs to, each with their role and member count.

GET /groups

Response

{
  "groups": [
    {
      "id": "0192d4e5-7a8b-7c6d-9e0f-1a2b3c4d5e6f",
      "slug": "design-team-a1b2c3d4",
      "name": "Design Team",
      "description": null,
      "logo": null,
      "ownerId": "user-uuid",
      "shareCode": null,
      "shareCanEdit": false,
      "createdAt": "2026-06-27T16:00:00.000Z",
      "updatedAt": "2026-06-27T16:00:00.000Z",
      "myRole": "OWNER",
      "memberCount": 3
    }
  ]
}

Errors

Status Description
401 Missing or invalid credentials

Add group members

Add or update members of a group by email. Owner-only — the caller must own the group. Membership is keyed by email, so an invitee who has not registered yet resolves to their account once they sign up with that email.

POST /groups/:id/members

Parameters

Parameter Type Description
id string The group's id (from create / list).

Request body

{
  "members": [
    { "email": "alice@example.com", "role": "CONTRIBUTOR" },
    { "email": "bob@example.com", "role": "VIEWER" }
  ]
}
Field Type Required Description
members array Yes 1–100 entries.
members[].email string Yes Member email.
members[].role string Yes One of OWNER, EDITOR, CONTRIBUTOR, VIEWER.

Response

Returns the upserted member rows:

{
  "members": [
    {
      "id": "0192d4e5-7a8b-7c6d-9e0f-1a2b3c4d5e6f",
      "groupId": "0192d4e5-7a8b-7c6d-9e0f-1a2b3c4d5e70",
      "principal": "alice@example.com",
      "role": "CONTRIBUTOR",
      "createdAt": "2026-06-27T16:00:00.000Z",
      "updatedAt": "2026-06-27T16:00:00.000Z"
    }
  ]
}

Errors

Status Description
401 Missing or invalid credentials
404 Group not found, or you are not its owner
422 Invalid request body

ProseMirror node types

Notes support the following node types in the content array:

Node type Description Example
paragraph A block of text { "type": "paragraph", "content": [{ "type": "text", "text": "..." }] }
heading Heading (attrs: level 1-6) { "type": "heading", "attrs": { "level": 1 }, "content": [...] }
bulletList Unordered list containing listItem nodes
orderedList Ordered list containing listItem nodes
listItem List item wrapping paragraph or nested list
taskList Todo list containing taskItem nodes
taskItem Todo item (attrs: checked boolean) { "type": "taskItem", "attrs": { "checked": false }, "content": [...] }
image Image (attrs: src) { "type": "image", "attrs": { "src": "https://..." } }
audio Audio recording (attrs: src, audioId)
relation Link to another note (attrs: id, label) { "type": "relation", "attrs": { "id": "note-uuid", "label": "My Note" } }
mention Inline @mention (attrs: id, label, type) { "type": "mention", "attrs": { "id": "entity-name", "label": "John" } }

Mentions are atomic inline nodes rendered as pills (e.g. @John). The id serves as the mention identifier and label is the display text. The type attribute defaults to "text" for free-text mentions.

Text marks

Text nodes can have marks for inline formatting:

Mark type Description
bold Bold text
italic Italic text
strike Strikethrough
underline Underline
link Hyperlink (attrs: href)
hashtagMark Hashtag (the text content includes the # prefix)

Example: rich note

{
  "content": {
    "type": "doc",
    "content": [
      {
        "type": "heading",
        "attrs": { "level": 1 },
        "content": [{ "type": "text", "text": "Meeting Notes" }]
      },
      {
        "type": "paragraph",
        "content": [
          { "type": "text", "text": "Discussed the " },
          { "type": "text", "text": "roadmap", "marks": [{ "type": "bold" }] },
          { "type": "text", "text": " for Q2." }
        ]
      },
      {
        "type": "taskList",
        "content": [
          {
            "type": "taskItem",
            "attrs": { "checked": false },
            "content": [
              {
                "type": "paragraph",
                "content": [{ "type": "text", "text": "Follow up with design team" }]
              }
            ]
          }
        ]
      }
    ]
  }
}