NoteStream AI API
All AI endpoints require an API key passed via the X-API-Key header. API keys can be created from Settings > API Keys in the NoteStream app.
X-API-Key: your-api-key-here
A note and its content are separate entities with a 1:1 mapping. A note holds metadata (ID, author, permissions, timestamps), while 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 for notes by text content. Returns matching note IDs with a plain text snippet (first 300 characters). Results are paginated with a fixed page size of 100.
GET /ai/search?query=meeting&page=1
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
query | string | Yes | The search text. Case-insensitive substring match. |
page | number | No | Page number (starts at 1). Defaults to 1. |
Response
{
"results": [
{
"noteId": "0192d4e5-7a8b-7c6d-9e0f-1a2b3c4d5e6f",
"snippet": "Meeting notes from the Q2 planning session..."
}
],
"pagination": {
"page": 1,
"pageSize": 100,
"totalResults": 250,
"totalPages": 3,
"hasNext": true,
"hasPrevious": false
}
}
Errors
| Status | Description |
|---|---|
| 401 | Missing or invalid API key |
| 422 | Missing or invalid query parameter |
Get a note
Retrieve the metadata of a note.
GET /ai/note/:id
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id | path | string | The note ID (UUID) |
Response
{
"id": "0192d4e5-7a8b-7c6d-9e0f-1a2b3c4d5e6f",
"authorId": "user-uuid",
"isPublished": false,
"publishedAt": null,
"accessMode": "EDITABLE",
"accessEmails": [],
"pinnedPosition": null,
"folderId": null,
"deletedAt": null,
"createdAt": 1711900800000,
"updatedAt": 1711900800000
}
Errors
| Status | Description |
|---|---|
| 401 | Missing or invalid API key |
| 404 | Note not found or not owned by the API key's user |
Update a note
Update the metadata of an existing note. All fields are optional.
PATCH /ai/note/:id
Parameters
| Name | In | Type | Description |
|---|---|---|---|
id | path | string | The note ID (UUID) |
Request body
{
"isPublished": true,
"accessMode": "READONLY",
"accessEmails": ["collaborator@example.com"]
}
| Field | Type | Required | Description |
|---|---|---|---|
isPublished | boolean | No | Publish or unpublish the note. |
accessMode | string | No | "EDITABLE" or "READONLY". |
accessEmails | string[] | No | List of email addresses that can access the note. |
Response
Returns the updated note object (same shape as GET /ai/note/:id).
Errors
| Status | Description |
|---|---|
| 401 | Missing or invalid API key |
| 404 | Note not found or not owned by the API key's user |
| 422 | Invalid request body |
Note: When a note is published (isPublished: true), it becomes accessible via a shareable link. The URL is constructed by removing 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.
Read note content
Retrieve the content of a note as ProseMirror JSON.
GET /ai/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 API key |
| 404 | Note not found or not owned by the API key's user |
Replace note content
Replace the entire content of an existing note with new ProseMirror JSON.
PUT /ai/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 API key |
| 404 | Note not found or not owned by the API key's user |
| 422 | Invalid ProseMirror JSON structure |
Create a note
Create a new note with the given ProseMirror JSON content. The note is assigned to the user who owns the API key.
POST /ai/note
Request body
{
"content": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "A brand new note" }
]
}
]
},
"isPublished": true
}
| Field | Type | Required | Description |
|---|---|---|---|
content | object | Yes | ProseMirror document with type: "doc" |
isPublished | boolean | No | Set to true to publish the note on creation. Defaults to false. |
accessMode | string | No | "EDITABLE" or "READONLY". Defaults to "EDITABLE". |
accessEmails | string[] | No | List of email addresses that can access the note. Defaults to [] (no restrictions). |
Response
Returns the full note object:
{
"id": "0192d4e5-7a8b-7c6d-9e0f-1a2b3c4d5e6f",
"authorId": "user-uuid",
"isPublished": true,
"publishedAt": 1711900800000,
"accessMode": "EDITABLE",
"accessEmails": [],
"folderId": null,
"createdAt": 1711900800000,
"updatedAt": 1711900800000
}
Errors
| Status | Description |
|---|---|
| 401 | Missing or invalid API key |
| 422 | Invalid ProseMirror JSON structure |
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" } } |
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" }]
}
]
}
]
}
]
}
}