Skip to content

API & API Keys

Use the StashSync REST API to read and write your data programmatically. Create API keys to authenticate from scripts, automations, and integrations.

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 typeFormatUse case
Clerk JWTShort-lived session tokenWeb app / real-time sync
API keyss_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

  1. Open Settings in the sidebar.
  2. Go to the API Keys tab.
  3. Click Create API key and enter a name (e.g. n8n, my-script).
  4. Copy the key shown — it starts with ss_live_ and is displayed only once.
  5. Store it somewhere safe (a password manager or secrets vault).
The raw key cannot be retrieved after you close the creation dialog. If you lose it, revoke the key and create a new one.

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

MethodPathDescription
GET/v1/notesList notes
POST/v1/notesCreate a note
GET/v1/notes/:idGet a single note
PATCH/v1/notes/:idUpdate a note
DELETE/v1/notes/:idTrash a note (soft-delete)
POST/v1/notes/:id/restoreRestore a trashed note

The note object

FieldTypeDescription
idstringUUID of the note
titlestringNote title
contentstringNote body (Markdown)
stashIdstring | nullUUID of the assigned stash, or null
pinnedbooleanWhether the note is pinned
trashedAtnumber | nullTimestamp (ms since epoch) when trashed, or null if active
tagsTag[]Tags applied to this note
createdAtnumberCreation timestamp (milliseconds since epoch)
updatedAtnumberLast-updated timestamp (milliseconds since epoch)

Each Tag in the tags array:

FieldTypeDescription
idstringTag UUID
namestringTag display name
colorstring | nullTag 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

ParameterTypeDefaultDescription
limitnumber50Max results to return. Maximum 100.
offsetnumber0Number of results to skip (for pagination).
stash_idstringFilter by stash UUID. Omit to return notes from all stashes.
tag_idstringFilter by tag UUID. Omit to return notes with any tag.
trashedbooleanfalseSet 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

ParameterTypeDescription
idstringUUID 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

StatusMeaning
404Note 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.

Free-plan accounts are limited to 100 total items (notes + bookmarks combined). Attempting to create a note when this limit is reached returns 403.

Request body

FieldTypeRequiredDescription
titlestringNoNote title. Leading/trailing whitespace is trimmed. Defaults to "Untitled". Max 500 characters.
contentstringNoNote body (Markdown). Defaults to "". Max 500,000 characters.
stashIdstring | nullNoUUID of the stash to assign. Pass null or omit to leave unstashed.
tagIdsstring[]NoArray 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

StatusMeaning
400Invalid JSON body
403Free-plan item limit reached (100 items max)
422Validation 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

ParameterTypeDescription
idstringUUID of the note

Request body — all fields optional

FieldTypeDescription
titlestringNew title. Leading/trailing whitespace is trimmed. Max 500 characters.
contentstringNew body (Markdown). Fully replaces the existing content. Max 500,000 characters.
stashIdstring | nullReassign to a different stash UUID, or null to remove from stash.
pinnedbooleantrue to pin the note, false to unpin.
tagIdsstring[]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

StatusMeaning
400Invalid JSON body
404Note 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

ParameterTypeDescription
idstringUUID of the note

Response

{ "ok": true }

Errors

StatusMeaning
404Note not found
409Note 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

ParameterTypeDescription
idstringUUID of the note

Response

{ "ok": true }

Errors

StatusMeaning
404Note not found
409Note 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

MethodPathDescription
GET/v1/bookmarksList bookmarks
POST/v1/bookmarksAdd a bookmark
GET/v1/bookmarks/:idGet a single bookmark
PATCH/v1/bookmarks/:idUpdate a bookmark
DELETE/v1/bookmarks/:idTrash a bookmark (soft-delete)
POST/v1/bookmarks/:id/restoreRestore a trashed bookmark

The bookmark object

FieldTypeDescription
idstringUUID of the bookmark
urlstringThe saved URL
titlestring | nullPage title, or null if not set
descriptionstring | nullPage description, or null if not set
imageUrlstring | nullPreview image URL (e.g. og:image), or null
stashIdstring | nullUUID of the assigned stash, or null
pinnedbooleanWhether the bookmark is pinned
trashedAtnumber | nullTimestamp (ms since epoch) when trashed, or null if active
tagsTag[]Tags applied to this bookmark
createdAtnumberCreation timestamp (milliseconds since epoch)
updatedAtnumberLast-updated timestamp (milliseconds since epoch)

Each Tag in the tags array:

FieldTypeDescription
idstringTag UUID
namestringTag display name
colorstring | nullTag 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

ParameterTypeDefaultDescription
limitnumber50Max results to return. Maximum 100.
offsetnumber0Number of results to skip (for pagination).
stash_idstringFilter by stash UUID. Omit to return bookmarks from all stashes.
tag_idstringFilter by tag UUID. Omit to return bookmarks with any tag.
trashedbooleanfalseSet 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

ParameterTypeDescription
idstringUUID 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

StatusMeaning
404Bookmark 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.

Free-plan accounts are limited to 100 total items (notes + bookmarks combined). Attempting to create a bookmark when this limit is reached returns 403.

Request body

FieldTypeRequiredDescription
urlstringYesThe URL to save. Must be a valid URL (validated with new URL()). Leading/trailing whitespace is trimmed.
titlestringNoPage title. Leading/trailing whitespace is trimmed. Defaults to null.
descriptionstringNoPage description. Leading/trailing whitespace is trimmed. Defaults to null.
imageUrlstring | nullNoPreview image URL. Defaults to null.
stashIdstring | nullNoUUID of the stash to assign. Pass null or omit to leave unstashed.
tagIdsstring[]NoArray 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

StatusMeaning
400Invalid JSON body
403Free-plan item limit reached (100 items max)
422url 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

ParameterTypeDescription
idstringUUID of the bookmark

Request body — all fields optional

FieldTypeDescription
urlstringNew URL. Must be a valid URL if provided. Leading/trailing whitespace is trimmed.
titlestringNew title. Leading/trailing whitespace is trimmed.
descriptionstringNew description. Leading/trailing whitespace is trimmed.
imageUrlstring | nullNew preview image URL, or null to clear it.
stashIdstring | nullReassign to a different stash UUID, or null to remove from stash.
pinnedbooleantrue to pin the bookmark, false to unpin.
tagIdsstring[]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

StatusMeaning
400Invalid JSON body
404Bookmark not found or already deleted
422url 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

ParameterTypeDescription
idstringUUID of the bookmark

Response

{ "ok": true }

Errors

StatusMeaning
404Bookmark not found
409Bookmark 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

ParameterTypeDescription
idstringUUID of the bookmark

Response

{ "ok": true }

Errors

StatusMeaning
404Bookmark not found
409Bookmark 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

MethodPathDescription
GET/v1/stashesList all stashes
POST/v1/stashesCreate a stash
GET/v1/stashes/:idGet a single stash
PATCH/v1/stashes/:idUpdate a stash
DELETE/v1/stashes/:idTrash a stash (soft-delete; items inside are not deleted)
POST/v1/stashes/:id/restoreRestore a trashed stash
GET/v1/stashes/:id/itemsList notes and bookmarks inside a stash

The stash object

FieldTypeDescription
idstringUUID of the stash
namestringStash name
colorstring | nullColor (hex string, e.g. "#3b82f6"), or null
createdAtnumberCreation timestamp (milliseconds since epoch)

GET /v1/stashes

List all stashes belonging to the authenticated user. Returns only active (non-trashed) stashes.

Query parameters

ParameterTypeDefaultDescription
limitnumber50Max results to return. Maximum 100.
offsetnumber0Number 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

ParameterTypeDescription
idstringUUID of the stash

Response — the stash object

{
  "id": "s1a2b3c4-d5e6-7890-abcd-ef1234567890",
  "name": "Work",
  "color": "#3b82f6",
  "createdAt": 1746700000000
}

Errors

StatusMeaning
404Stash 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

FieldTypeRequiredDescription
namestringYesStash name. Leading/trailing whitespace is trimmed. Max 200 characters. Cannot be empty.
colorstring | nullNoColor 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

StatusMeaning
400Invalid 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

ParameterTypeDescription
idstringUUID of the stash

Request body — all fields optional

FieldTypeDescription
namestringNew name. Leading/trailing whitespace is trimmed. Max 200 characters. Cannot be set to an empty string.
colorstring | nullNew color, or null to clear it.

Response — the updated stash object

Errors

StatusMeaning
400Invalid JSON body, or name is provided but empty
404Stash 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

ParameterTypeDescription
idstringUUID of the stash

Response

{ "ok": true }

Errors

StatusMeaning
404Stash not found
409Stash 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

ParameterTypeDescription
idstringUUID of the stash

Response

{ "ok": true }

Errors

StatusMeaning
404Stash not found
409Stash 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

ParameterTypeDescription
idstringUUID of the stash

Query parameters

ParameterTypeDefaultDescription
limitnumber50Max results to return. Maximum 100.
offsetnumber0Number 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
}
FieldTypeDescription
type"note" | "bookmark"The item type
idstringUUID of the item
titlestring | nullItem title, or null if not set
updatedAtnumberLast-updated timestamp (milliseconds since epoch)

Errors

StatusMeaning
404Stash not found

Example

curl https://api.stashsync.app/v1/stashes/s1a2b3c4-d5e6-7890-abcd-ef1234567890/items \
  -H "Authorization: Bearer ss_live_YOUR_KEY"

Tags

MethodPathDescription
GET/v1/tagsList all tags
POST/v1/tagsCreate a tag
GET/v1/tags/:idGet a single tag
PATCH/v1/tags/:idUpdate a tag
DELETE/v1/tags/:idTrash a tag (soft-delete; tagged items are not deleted)
POST/v1/tags/:id/restoreRestore a trashed tag
GET/v1/tags/:id/itemsList notes and bookmarks with this tag

The tag object

FieldTypeDescription
idstringUUID of the tag
namestringTag display name
colorstring | nullColor (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

ParameterTypeDescription
idstringUUID of the tag

Response — the tag object

{
  "id": "t1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "reading",
  "color": "#10b981"
}

Errors

StatusMeaning
404Tag 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

FieldTypeRequiredDescription
namestringYesTag name. Leading/trailing whitespace is trimmed. Max 100 characters. Cannot be empty.
colorstring | nullNoColor 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

StatusMeaning
400Invalid 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

ParameterTypeDescription
idstringUUID of the tag

Request body — all fields optional

FieldTypeDescription
namestringNew name. Leading/trailing whitespace is trimmed. Max 100 characters. Cannot be set to an empty string.
colorstring | nullNew color, or null to clear it.

Response — the updated tag object

Errors

StatusMeaning
400Invalid JSON body, or name is provided but empty
404Tag 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

ParameterTypeDescription
idstringUUID of the tag

Response

{ "ok": true }

Errors

StatusMeaning
404Tag not found
409Tag 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

ParameterTypeDescription
idstringUUID of the tag

Response

{ "ok": true }

Errors

StatusMeaning
404Tag not found
409Tag 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

ParameterTypeDescription
idstringUUID of the tag

Query parameters

ParameterTypeDefaultDescription
limitnumber50Max results to return. Maximum 100.
offsetnumber0Number 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
}
FieldTypeDescription
type"note" | "bookmark"The item type
idstringUUID of the item
titlestring | nullItem title, or null if not set
updatedAtnumberLast-updated timestamp (milliseconds since epoch)

Errors

StatusMeaning
404Tag not found

Example

curl https://api.stashsync.app/v1/tags/t1b2c3d4-e5f6-7890-abcd-ef1234567890/items \
  -H "Authorization: Bearer ss_live_YOUR_KEY"

MethodPathDescription
GET/v1/searchFull-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

ParameterTypeRequiredDescription
qstringYesThe 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…"
  }
]
FieldTypeDescription
idstringUUID of the matching item
type"note" | "bookmark" | "file"The item type
titlestringTitle of the matching item
snippetstringA short excerpt (up to 20 words) around the match. The matched term is wrapped in <mark>…</mark> tags.

Search scope

TypeFields searched
Notestitle, body content
Bookmarkstitle, description
Filesfilename, MIME type

Errors

StatusMeaning
500Search 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"