Overview
The StashSync REST API (https://api.stashsync.app/v1/*) gives programmatic access to all your notes, bookmarks, stashes, and tags. It's designed for automation tools like n8n and Zapier, custom scripts, and third-party integrations.
All endpoints require a Bearer token in the Authorization header:
Authorization: Bearer <token>
Two token types are accepted:
| Token type | Format | Use case |
|---|---|---|
| Clerk JWT | Short-lived session token | Web app / real-time sync |
| API key | ss_live_<hex> | Scripts, automations, MCP, long-lived integrations |
For external integrations, always use an API key. The raw key is shown once at creation and cannot be retrieved again.
Rate limit: 120 requests per minute per API key.
Creating an API key
- Open Settings in the sidebar.
- Go to the API Keys tab.
- Click Create API key and enter a name (e.g.
n8n,my-script). - Copy the key shown — it starts with
ss_live_and is displayed only once. - Store it somewhere safe (a password manager or secrets vault).
You can have up to 10 active API keys per account. To revoke a key, click the trash icon next to it — revocation is immediate.
REST API reference
Notes
| Method | Path | Description |
|---|---|---|
GET | /v1/notes | List notes |
POST | /v1/notes | Create a note |
GET | /v1/notes/:id | Get a single note |
PATCH | /v1/notes/:id | Update a note |
DELETE | /v1/notes/:id | Trash a note (soft-delete) |
POST | /v1/notes/:id/restore | Restore a trashed note |
The note object
| Field | Type | Description |
|---|---|---|
id | string | UUID of the note |
title | string | Note title |
content | string | Note body (Markdown) |
stashId | string | null | UUID of the assigned stash, or null |
pinned | boolean | Whether the note is pinned |
trashedAt | number | null | Timestamp (ms since epoch) when trashed, or null if active |
tags | Tag[] | Tags applied to this note |
createdAt | number | Creation timestamp (milliseconds since epoch) |
updatedAt | number | Last-updated timestamp (milliseconds since epoch) |
Each Tag in the tags array:
| Field | Type | Description |
|---|---|---|
id | string | Tag UUID |
name | string | Tag display name |
color | string | null | Tag color (hex string, e.g. "#3b82f6"), or null |
GET /v1/notes
List notes belonging to the authenticated user. Returns active (non-trashed) notes by default.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Max results to return. Maximum 100. |
offset | number | 0 | Number of results to skip (for pagination). |
stash_id | string | — | Filter by stash UUID. Omit to return notes from all stashes. |
tag_id | string | — | Filter by tag UUID. Omit to return notes with any tag. |
trashed | boolean | false | Set to true to list only trashed notes. |
Response
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "Meeting notes",
"content": "## Agenda\n- Item 1\n- Item 2",
"stashId": "e5f6a7b8-c9d0-1234-efab-567890abcdef",
"pinned": false,
"trashedAt": null,
"tags": [
{ "id": "t1b2c3d4-...", "name": "work", "color": "#3b82f6" }
],
"createdAt": 1746700000000,
"updatedAt": 1746700000000
}
],
"total": 42,
"limit": 50,
"offset": 0
}
Example
# All active notes
curl https://api.stashsync.app/v1/notes \
-H "Authorization: Bearer ss_live_YOUR_KEY"
# Notes in a specific stash, paginated
curl "https://api.stashsync.app/v1/notes?stash_id=e5f6a7b8-...&limit=20&offset=40" \
-H "Authorization: Bearer ss_live_YOUR_KEY"
# Trashed notes only
curl "https://api.stashsync.app/v1/notes?trashed=true" \
-H "Authorization: Bearer ss_live_YOUR_KEY"
GET /v1/notes/:id
Get a single note by ID.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the note |
Response — the note object
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "Meeting notes",
"content": "## Agenda\n- Item 1\n- Item 2",
"stashId": null,
"pinned": false,
"trashedAt": null,
"tags": [],
"createdAt": 1746700000000,
"updatedAt": 1746700000000
}
Errors
| Status | Meaning |
|---|---|
404 | Note not found or belongs to another user |
Example
curl https://api.stashsync.app/v1/notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer ss_live_YOUR_KEY"
POST /v1/notes
Create a new note.
403.Request body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | No | Note title. Leading/trailing whitespace is trimmed. Defaults to "Untitled". Max 500 characters. |
content | string | No | Note body (Markdown). Defaults to "". Max 500,000 characters. |
stashId | string | null | No | UUID of the stash to assign. Pass null or omit to leave unstashed. |
tagIds | string[] | No | Array of tag UUIDs to apply. Defaults to []. |
Response 201 Created — the note object
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "Meeting notes",
"content": "## Agenda\n- Item 1\n- Item 2",
"stashId": null,
"pinned": false,
"trashedAt": null,
"tags": [],
"createdAt": 1746700000000,
"updatedAt": 1746700000000
}
Errors
| Status | Meaning |
|---|---|
400 | Invalid JSON body |
403 | Free-plan item limit reached (100 items max) |
422 | Validation error (e.g. title exceeds 500 characters) |
Example
curl -X POST https://api.stashsync.app/v1/notes \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Meeting notes",
"content": "## Agenda\n- Item 1\n- Item 2",
"stashId": "e5f6a7b8-c9d0-1234-efab-567890abcdef",
"tagIds": ["t1b2c3d4-e5f6-7890-abcd-ef1234567890"]
}'
PATCH /v1/notes/:id
Update one or more fields on an existing note. Only the fields you include are changed — omitted fields are left as-is.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the note |
Request body — all fields optional
| Field | Type | Description |
|---|---|---|
title | string | New title. Leading/trailing whitespace is trimmed. Max 500 characters. |
content | string | New body (Markdown). Fully replaces the existing content. Max 500,000 characters. |
stashId | string | null | Reassign to a different stash UUID, or null to remove from stash. |
pinned | boolean | true to pin the note, false to unpin. |
tagIds | string[] | Replaces the note's entire tag set with this array of tag UUIDs. |
updatedAt is automatically set to the current timestamp on every successful update.
Response — the updated note object
Errors
| Status | Meaning |
|---|---|
400 | Invalid JSON body |
404 | Note not found or already deleted |
Example
# Pin the note and move it to a different stash
curl -X PATCH https://api.stashsync.app/v1/notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"pinned": true,
"stashId": "e5f6a7b8-c9d0-1234-efab-567890abcdef"
}'
# Update only the title
curl -X PATCH https://api.stashsync.app/v1/notes/a1b2c3d4-... \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "title": "Updated title" }'
DELETE /v1/notes/:id
Move a note to trash (soft-delete). The note is not permanently removed and can be restored at any time.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the note |
Response
{ "ok": true }
Errors
| Status | Meaning |
|---|---|
404 | Note not found |
409 | Note is already in trash |
Example
curl -X DELETE https://api.stashsync.app/v1/notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer ss_live_YOUR_KEY"
POST /v1/notes/:id/restore
Restore a trashed note, making it active again.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the note |
Response
{ "ok": true }
Errors
| Status | Meaning |
|---|---|
404 | Note not found |
409 | Note is not in trash |
Example
curl -X POST https://api.stashsync.app/v1/notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890/restore \
-H "Authorization: Bearer ss_live_YOUR_KEY"
Bookmarks
| Method | Path | Description |
|---|---|---|
GET | /v1/bookmarks | List bookmarks |
POST | /v1/bookmarks | Add a bookmark |
GET | /v1/bookmarks/:id | Get a single bookmark |
PATCH | /v1/bookmarks/:id | Update a bookmark |
DELETE | /v1/bookmarks/:id | Trash a bookmark (soft-delete) |
POST | /v1/bookmarks/:id/restore | Restore a trashed bookmark |
The bookmark object
| Field | Type | Description |
|---|---|---|
id | string | UUID of the bookmark |
url | string | The saved URL |
title | string | null | Page title, or null if not set |
description | string | null | Page description, or null if not set |
imageUrl | string | null | Preview image URL (e.g. og:image), or null |
stashId | string | null | UUID of the assigned stash, or null |
pinned | boolean | Whether the bookmark is pinned |
trashedAt | number | null | Timestamp (ms since epoch) when trashed, or null if active |
tags | Tag[] | Tags applied to this bookmark |
createdAt | number | Creation timestamp (milliseconds since epoch) |
updatedAt | number | Last-updated timestamp (milliseconds since epoch) |
Each Tag in the tags array:
| Field | Type | Description |
|---|---|---|
id | string | Tag UUID |
name | string | Tag display name |
color | string | null | Tag color (hex string, e.g. "#3b82f6"), or null |
GET /v1/bookmarks
List bookmarks belonging to the authenticated user. Returns active (non-trashed) bookmarks by default, ordered by updatedAt descending.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Max results to return. Maximum 100. |
offset | number | 0 | Number of results to skip (for pagination). |
stash_id | string | — | Filter by stash UUID. Omit to return bookmarks from all stashes. |
tag_id | string | — | Filter by tag UUID. Omit to return bookmarks with any tag. |
trashed | boolean | false | Set to true to list only trashed bookmarks. |
Response
{
"data": [
{
"id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
"url": "https://example.com/article",
"title": "An Interesting Article",
"description": "A summary of the article content.",
"imageUrl": "https://example.com/og-image.png",
"stashId": "e5f6a7b8-c9d0-1234-efab-567890abcdef",
"pinned": false,
"trashedAt": null,
"tags": [
{ "id": "t1b2c3d4-...", "name": "reading", "color": "#10b981" }
],
"createdAt": 1746700000000,
"updatedAt": 1746700000000
}
],
"total": 18,
"limit": 50,
"offset": 0
}
Example
# All active bookmarks
curl https://api.stashsync.app/v1/bookmarks \
-H "Authorization: Bearer ss_live_YOUR_KEY"
# Bookmarks in a specific stash
curl "https://api.stashsync.app/v1/bookmarks?stash_id=e5f6a7b8-...&limit=20" \
-H "Authorization: Bearer ss_live_YOUR_KEY"
# Trashed bookmarks only
curl "https://api.stashsync.app/v1/bookmarks?trashed=true" \
-H "Authorization: Bearer ss_live_YOUR_KEY"
GET /v1/bookmarks/:id
Get a single bookmark by ID.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the bookmark |
Response — the bookmark object
{
"id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
"url": "https://example.com/article",
"title": "An Interesting Article",
"description": "A summary of the article content.",
"imageUrl": "https://example.com/og-image.png",
"stashId": null,
"pinned": false,
"trashedAt": null,
"tags": [],
"createdAt": 1746700000000,
"updatedAt": 1746700000000
}
Errors
| Status | Meaning |
|---|---|
404 | Bookmark not found or belongs to another user |
Example
curl https://api.stashsync.app/v1/bookmarks/b1c2d3e4-f5a6-7890-bcde-f12345678901 \
-H "Authorization: Bearer ss_live_YOUR_KEY"
POST /v1/bookmarks
Save a new bookmark.
403.Request body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The URL to save. Must be a valid URL (validated with new URL()). Leading/trailing whitespace is trimmed. |
title | string | No | Page title. Leading/trailing whitespace is trimmed. Defaults to null. |
description | string | No | Page description. Leading/trailing whitespace is trimmed. Defaults to null. |
imageUrl | string | null | No | Preview image URL. Defaults to null. |
stashId | string | null | No | UUID of the stash to assign. Pass null or omit to leave unstashed. |
tagIds | string[] | No | Array of tag UUIDs to apply. Defaults to []. |
Response 201 Created — the bookmark object
{
"id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
"url": "https://example.com/article",
"title": "An Interesting Article",
"description": "A summary of the article content.",
"imageUrl": null,
"stashId": null,
"pinned": false,
"trashedAt": null,
"tags": [],
"createdAt": 1746700000000,
"updatedAt": 1746700000000
}
Errors
| Status | Meaning |
|---|---|
400 | Invalid JSON body |
403 | Free-plan item limit reached (100 items max) |
422 | url is missing or not a valid URL |
Example
# Minimal — URL only
curl -X POST https://api.stashsync.app/v1/bookmarks \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/article" }'
# With metadata, stash, and tags
curl -X POST https://api.stashsync.app/v1/bookmarks \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/article",
"title": "An Interesting Article",
"description": "A summary of the article content.",
"imageUrl": "https://example.com/og-image.png",
"stashId": "e5f6a7b8-c9d0-1234-efab-567890abcdef",
"tagIds": ["t1b2c3d4-e5f6-7890-abcd-ef1234567890"]
}'
PATCH /v1/bookmarks/:id
Update one or more fields on an existing bookmark. Only the fields you include are changed — omitted fields are left as-is.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the bookmark |
Request body — all fields optional
| Field | Type | Description |
|---|---|---|
url | string | New URL. Must be a valid URL if provided. Leading/trailing whitespace is trimmed. |
title | string | New title. Leading/trailing whitespace is trimmed. |
description | string | New description. Leading/trailing whitespace is trimmed. |
imageUrl | string | null | New preview image URL, or null to clear it. |
stashId | string | null | Reassign to a different stash UUID, or null to remove from stash. |
pinned | boolean | true to pin the bookmark, false to unpin. |
tagIds | string[] | Replaces the bookmark's entire tag set with this array of tag UUIDs. |
updatedAt is automatically set to the current timestamp on every successful update.
Response — the updated bookmark object
Errors
| Status | Meaning |
|---|---|
400 | Invalid JSON body |
404 | Bookmark not found or already deleted |
422 | url field provided but is not a valid URL |
Example
# Update the title and pin the bookmark
curl -X PATCH https://api.stashsync.app/v1/bookmarks/b1c2d3e4-f5a6-7890-bcde-f12345678901 \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Updated Title",
"pinned": true
}'
# Clear the preview image and remove from stash
curl -X PATCH https://api.stashsync.app/v1/bookmarks/b1c2d3e4-... \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "imageUrl": null, "stashId": null }'
DELETE /v1/bookmarks/:id
Move a bookmark to trash (soft-delete). The bookmark is not permanently removed and can be restored at any time.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the bookmark |
Response
{ "ok": true }
Errors
| Status | Meaning |
|---|---|
404 | Bookmark not found |
409 | Bookmark is already in trash |
Example
curl -X DELETE https://api.stashsync.app/v1/bookmarks/b1c2d3e4-f5a6-7890-bcde-f12345678901 \
-H "Authorization: Bearer ss_live_YOUR_KEY"
POST /v1/bookmarks/:id/restore
Restore a trashed bookmark, making it active again.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the bookmark |
Response
{ "ok": true }
Errors
| Status | Meaning |
|---|---|
404 | Bookmark not found |
409 | Bookmark is not in trash |
Example
curl -X POST https://api.stashsync.app/v1/bookmarks/b1c2d3e4-f5a6-7890-bcde-f12345678901/restore \
-H "Authorization: Bearer ss_live_YOUR_KEY"
Stashes
| Method | Path | Description |
|---|---|---|
GET | /v1/stashes | List all stashes |
POST | /v1/stashes | Create a stash |
GET | /v1/stashes/:id | Get a single stash |
PATCH | /v1/stashes/:id | Update a stash |
DELETE | /v1/stashes/:id | Trash a stash (soft-delete; items inside are not deleted) |
POST | /v1/stashes/:id/restore | Restore a trashed stash |
GET | /v1/stashes/:id/items | List notes and bookmarks inside a stash |
The stash object
| Field | Type | Description |
|---|---|---|
id | string | UUID of the stash |
name | string | Stash name |
color | string | null | Color (hex string, e.g. "#3b82f6"), or null |
createdAt | number | Creation timestamp (milliseconds since epoch) |
GET /v1/stashes
List all stashes belonging to the authenticated user. Returns only active (non-trashed) stashes.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Max results to return. Maximum 100. |
offset | number | 0 | Number of results to skip (for pagination). |
Response
{
"data": [
{
"id": "s1a2b3c4-d5e6-7890-abcd-ef1234567890",
"name": "Work",
"color": "#3b82f6",
"createdAt": 1746700000000
}
],
"total": 5,
"limit": 50,
"offset": 0
}
Example
curl https://api.stashsync.app/v1/stashes \
-H "Authorization: Bearer ss_live_YOUR_KEY"
GET /v1/stashes/:id
Get a single stash by ID.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the stash |
Response — the stash object
{
"id": "s1a2b3c4-d5e6-7890-abcd-ef1234567890",
"name": "Work",
"color": "#3b82f6",
"createdAt": 1746700000000
}
Errors
| Status | Meaning |
|---|---|
404 | Stash not found or belongs to another user |
Example
curl https://api.stashsync.app/v1/stashes/s1a2b3c4-d5e6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer ss_live_YOUR_KEY"
POST /v1/stashes
Create a new stash.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Stash name. Leading/trailing whitespace is trimmed. Max 200 characters. Cannot be empty. |
color | string | null | No | Color value (e.g. a hex string like "#3b82f6"). Defaults to null. |
Response 201 Created — the stash object
{
"id": "s1a2b3c4-d5e6-7890-abcd-ef1234567890",
"name": "Work",
"color": "#3b82f6",
"createdAt": 1746700000000
}
Errors
| Status | Meaning |
|---|---|
400 | Invalid JSON body, or name is missing or empty |
Example
curl -X POST https://api.stashsync.app/v1/stashes \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Work", "color": "#3b82f6" }'
PATCH /v1/stashes/:id
Update one or more fields on an existing stash. Only the fields you include are changed.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the stash |
Request body — all fields optional
| Field | Type | Description |
|---|---|---|
name | string | New name. Leading/trailing whitespace is trimmed. Max 200 characters. Cannot be set to an empty string. |
color | string | null | New color, or null to clear it. |
Response — the updated stash object
Errors
| Status | Meaning |
|---|---|
400 | Invalid JSON body, or name is provided but empty |
404 | Stash not found or already trashed |
Example
# Rename and recolor
curl -X PATCH https://api.stashsync.app/v1/stashes/s1a2b3c4-d5e6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Work Projects", "color": "#10b981" }'
# Clear the color
curl -X PATCH https://api.stashsync.app/v1/stashes/s1a2b3c4-... \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "color": null }'
DELETE /v1/stashes/:id
Move a stash to trash (soft-delete). Items inside the stash are not deleted — they lose their stash assignment and return to the main view.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the stash |
Response
{ "ok": true }
Errors
| Status | Meaning |
|---|---|
404 | Stash not found |
409 | Stash is already in trash |
Example
curl -X DELETE https://api.stashsync.app/v1/stashes/s1a2b3c4-d5e6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer ss_live_YOUR_KEY"
POST /v1/stashes/:id/restore
Restore a trashed stash, making it active again.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the stash |
Response
{ "ok": true }
Errors
| Status | Meaning |
|---|---|
404 | Stash not found |
409 | Stash is not in trash |
Example
curl -X POST https://api.stashsync.app/v1/stashes/s1a2b3c4-d5e6-7890-abcd-ef1234567890/restore \
-H "Authorization: Bearer ss_live_YOUR_KEY"
GET /v1/stashes/:id/items
List notes and bookmarks that belong to a stash. Only active (non-trashed) items are returned.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the stash |
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Max results to return. Maximum 100. |
offset | number | 0 | Number of results to skip (for pagination). |
Response
Each item in data is a lightweight summary — use GET /v1/notes/:id or GET /v1/bookmarks/:id to fetch full details.
{
"data": [
{
"type": "note",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "Meeting notes",
"updatedAt": 1746700000000
},
{
"type": "bookmark",
"id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
"title": "An Interesting Article",
"updatedAt": 1746699000000
}
],
"total": 12,
"limit": 50,
"offset": 0
}
| Field | Type | Description |
|---|---|---|
type | "note" | "bookmark" | The item type |
id | string | UUID of the item |
title | string | null | Item title, or null if not set |
updatedAt | number | Last-updated timestamp (milliseconds since epoch) |
Errors
| Status | Meaning |
|---|---|
404 | Stash not found |
Example
curl https://api.stashsync.app/v1/stashes/s1a2b3c4-d5e6-7890-abcd-ef1234567890/items \
-H "Authorization: Bearer ss_live_YOUR_KEY"
Tags
| Method | Path | Description |
|---|---|---|
GET | /v1/tags | List all tags |
POST | /v1/tags | Create a tag |
GET | /v1/tags/:id | Get a single tag |
PATCH | /v1/tags/:id | Update a tag |
DELETE | /v1/tags/:id | Trash a tag (soft-delete; tagged items are not deleted) |
POST | /v1/tags/:id/restore | Restore a trashed tag |
GET | /v1/tags/:id/items | List notes and bookmarks with this tag |
The tag object
| Field | Type | Description |
|---|---|---|
id | string | UUID of the tag |
name | string | Tag display name |
color | string | null | Color (hex string, e.g. "#10b981"), or null |
GET /v1/tags
List all tags belonging to the authenticated user. Returns all active (non-trashed) tags sorted alphabetically by name. This endpoint returns all tags without pagination.
Response
{
"data": [
{ "id": "t1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "reading", "color": "#10b981" },
{ "id": "t2c3d4e5-f6a7-8901-bcde-f12345678902", "name": "work", "color": "#3b82f6" }
],
"total": 2
}
Example
curl https://api.stashsync.app/v1/tags \
-H "Authorization: Bearer ss_live_YOUR_KEY"
GET /v1/tags/:id
Get a single tag by ID.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the tag |
Response — the tag object
{
"id": "t1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "reading",
"color": "#10b981"
}
Errors
| Status | Meaning |
|---|---|
404 | Tag not found or belongs to another user |
Example
curl https://api.stashsync.app/v1/tags/t1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer ss_live_YOUR_KEY"
POST /v1/tags
Create a new tag.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Tag name. Leading/trailing whitespace is trimmed. Max 100 characters. Cannot be empty. |
color | string | null | No | Color value (e.g. a hex string like "#10b981"). Defaults to null. |
Response 201 Created — the tag object
{
"id": "t1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "reading",
"color": "#10b981"
}
Errors
| Status | Meaning |
|---|---|
400 | Invalid JSON body, or name is missing or empty |
Example
curl -X POST https://api.stashsync.app/v1/tags \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "reading", "color": "#10b981" }'
PATCH /v1/tags/:id
Update one or more fields on an existing tag. Only the fields you include are changed.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the tag |
Request body — all fields optional
| Field | Type | Description |
|---|---|---|
name | string | New name. Leading/trailing whitespace is trimmed. Max 100 characters. Cannot be set to an empty string. |
color | string | null | New color, or null to clear it. |
Response — the updated tag object
Errors
| Status | Meaning |
|---|---|
400 | Invalid JSON body, or name is provided but empty |
404 | Tag not found or already trashed |
Example
# Rename the tag
curl -X PATCH https://api.stashsync.app/v1/tags/t1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "to-read" }'
# Change the color
curl -X PATCH https://api.stashsync.app/v1/tags/t1b2c3d4-... \
-H "Authorization: Bearer ss_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "color": "#f59e0b" }'
DELETE /v1/tags/:id
Move a tag to trash (soft-delete). Items that use this tag are not deleted — they simply lose this tag association.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the tag |
Response
{ "ok": true }
Errors
| Status | Meaning |
|---|---|
404 | Tag not found |
409 | Tag is already in trash |
Example
curl -X DELETE https://api.stashsync.app/v1/tags/t1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer ss_live_YOUR_KEY"
POST /v1/tags/:id/restore
Restore a trashed tag, making it active again.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the tag |
Response
{ "ok": true }
Errors
| Status | Meaning |
|---|---|
404 | Tag not found |
409 | Tag is not in trash |
Example
curl -X POST https://api.stashsync.app/v1/tags/t1b2c3d4-e5f6-7890-abcd-ef1234567890/restore \
-H "Authorization: Bearer ss_live_YOUR_KEY"
GET /v1/tags/:id/items
List notes and bookmarks that have this tag applied. Only active (non-trashed) items are returned, ordered by most recently updated first.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | UUID of the tag |
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Max results to return. Maximum 100. |
offset | number | 0 | Number of results to skip (for pagination). |
Response
{
"data": [
{
"type": "note",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "Reading list",
"updatedAt": 1746700000000
},
{
"type": "bookmark",
"id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
"title": "An Interesting Article",
"updatedAt": 1746699000000
}
],
"total": 7,
"limit": 50,
"offset": 0
}
| Field | Type | Description |
|---|---|---|
type | "note" | "bookmark" | The item type |
id | string | UUID of the item |
title | string | null | Item title, or null if not set |
updatedAt | number | Last-updated timestamp (milliseconds since epoch) |
Errors
| Status | Meaning |
|---|---|
404 | Tag not found |
Example
curl https://api.stashsync.app/v1/tags/t1b2c3d4-e5f6-7890-abcd-ef1234567890/items \
-H "Authorization: Bearer ss_live_YOUR_KEY"
Search
| Method | Path | Description |
|---|---|---|
GET | /v1/search | Full-text search across notes, bookmarks, and files |
GET /v1/search
Search across all your notes, bookmarks, and files using full-text search. Results are ranked by relevance and capped at 20. Trashed and deleted items are always excluded.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | Yes | The search query. Leading/trailing whitespace is trimmed. Returns [] immediately if empty or omitted. |
Response — a flat array of up to 20 results (no pagination wrapper)
[
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "note",
"title": "Meeting notes",
"snippet": "…discussed the <mark>quarterly</mark> review and next steps…"
},
{
"id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
"type": "bookmark",
"title": "Q4 Planning Article",
"snippet": "…a guide to <mark>quarterly</mark> planning for remote teams…"
}
]
| Field | Type | Description |
|---|---|---|
id | string | UUID of the matching item |
type | "note" | "bookmark" | "file" | The item type |
title | string | Title of the matching item |
snippet | string | A short excerpt (up to 20 words) around the match. The matched term is wrapped in <mark>…</mark> tags. |
Search scope
| Type | Fields searched |
|---|---|
| Notes | title, body content |
| Bookmarks | title, description |
| Files | filename, MIME type |
Errors
| Status | Meaning |
|---|---|
500 | Search index query failed |
Example
curl "https://api.stashsync.app/v1/search?q=quarterly+review" \
-H "Authorization: Bearer ss_live_YOUR_KEY"
Pagination
List endpoints support limit (default 50, max 100) and offset for cursor-style pagination:
curl "https://api.stashsync.app/v1/notes?limit=20&offset=40" \
-H "Authorization: Bearer ss_live_YOUR_KEY"
The response always includes a total field — the count of all matching records before pagination — so you can calculate how many pages remain.
Filtering
Combine query parameters to narrow results:
# Notes in a specific stash tagged with a specific tag
curl "https://api.stashsync.app/v1/notes?stash_id=<stash_id>&tag_id=<tag_id>" \
-H "Authorization: Bearer ss_live_YOUR_KEY"