Agents & developers

API documentation

Connect Claude, Cursor, OpenClaw, or your own agent to Zegazone collections and media. Look up single items with media.get / collections.get, search with media.search, or resolve share URLs with collections.get_by_slug. Schema version 1.4.0.

Connect your AI agent

Two ways to connect — both expose identical tools. Choose based on your agent type.

Option 1 — Recommended

Remote MCP

No installation required. Works with IDE and cloud agents (Claude.ai Cloud, Cursor desktop, Cursor cloud agents).

Claude.ai Cloud

Add as a custom MCP connector in Claude.ai settings:

https://mcp.zegazone.com/mcp

Cursor (remote mode)

Add to ~/.cursor/mcp.json:

{
  "mcpServers": {
    "zegazone": {
      "url": "https://mcp.zegazone.com/mcp"
    }
  }
}

Save your MCP config, then reload MCP in your client (Cursor: Settings → MCP → refresh). Complete browser OAuth when prompted. Remote mode supports Claude.ai Cloud and Cursor (desktop and cloud agents). Done.

Troubleshooting: if the server shows an error (red), reload MCP and authorize again in the browser. Prefer remote for Cursor unless you are debugging the API — then use local MCP below.

Option 2 — Developers

Local MCP

For agents running on your local machine via stdio. Best for debugging the API or when remote OAuth is unavailable. Cursor users can use either remote (above) or local — remote is simpler (no install).

1. Pair your account

npx @zegazone_mcp/mcp@latest --pair

2. Add to MCP config

{
  "mcpServers": {
    "zegazone": {
      "command": "npx",
      "args": ["@zegazone_mcp/mcp@latest"]
    }
  }
}

Credentials stored locally in ~/.zegazone-mcp/credentials.json

Test your connection: Ask your agent: “Use Zegazone MCP to ping and tell me who I'm logged in as.”

Agent-driven UI

Your AI controls Zegazone

Agents do not only reply in chat — they can push results directly into your Zegazone player in real time. After browsing, searching, or filtering your library, an agent calls ui.state.set (MCP tool ui_state_set) to open collections, switch grid or carousel view, and select a specific media item. Changes broadcast to any tab where you have the player open.

  • Show me the most viewed public collections this week

    Agent browses public collections sorted by views, pushes results to your player live

  • Find public photography collections

    Agent searches for photography collections, displays them in your collections grid

  • Show me my collections with no tags

    Agent scans your library, filters untagged ones, shows them so you can work through them

  • Show me collections shared with me that I haven't opened

    Agent checks your shared collections, filters by not yet viewed, pushes the list

  • Open my boating collection and select the video called Holiday 2025

    Agent navigates the player to exactly that media item instantly

# Open a collection and select media (REST)
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"ui.state.set","collection_id":123,"media_id":456,"view_mode":"grid","source":"agent"}'

# Push browse results into the collections grid (live in the player)
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"ui.state.set","browse_results":[123,456,789],"browse_title":"Most viewed this week","browse_source":"agent","source":"agent"}'

Read current player context with ui.state.get / ui_state_get before navigating so the agent stays aligned with what you see.

OpenAPI 3.1

Machine-readable spec for tooling and codegen.

MCP server

Package @zegazone_mcp/mcp@latest — all 63 tools expose typed input schemas (field names, types, descriptions) plus lookup tools media_get, collections_get, and media_search.

REST entrypoint

POST https://api.zegaphone.com/functions/v1/thirdparty-v1

Local MCP installation

For Claude Desktop, Cursor local mode, and OpenClaw. Install with a single command — no repo clone required.

# One-time OAuth pairing (opens browser)
npx @zegazone_mcp/mcp@latest --pair

# Verify install (expect 2.0.5+)
npx @zegazone_mcp/mcp@latest --version

# After an API update: pin the new version in mcp.json, then reload MCP in your client

Your credentials are stored locally in ~/.zegazone-mcp/credentials.json and refresh automatically. Pin @zegazone_mcp/mcp@latest in your MCP config for typed tool schemas on every operation; use operations_list or schema_get to discover ops and fields.

Prefer remote MCP? If you're using Claude.ai Cloud or another cloud agent, use the remote MCP connector instead — no local installation needed.

Once connected, OpenClaw agents can control every aspect of Zegazone — creating collections, uploading media, managing sharing and collaborators, and controlling the UI — making it the most powerful way to use Zegazone with an AI agent.

# One-time OAuth pairing
npx @zegazone_mcp/mcp@latest --pair

# Add to OpenClaw config (pin version for named lookup tools)
openclaw config set mcpServers.zegazone.command "npx"
openclaw config set mcpServers.zegazone.args '["@zegazone_mcp/mcp@latest"]'

Or via OpenClaw settings JSON:

{
  "mcpServers": {
    "zegazone": {
      "command": "npx",
      "args": ["@zegazone_mcp/mcp@latest"]
    }
  }
}

OpenClaw supports both stdio and HTTP/SSE MCP transports. Zegazone uses the stdio transport via npx.

Discover capabilities: call operations_list, then operation_describe or schema_get for field schemas.

Lookup tools: media_get, collections_get, collections_get_by_slug, media_search.

Typed schemas (2.0.5+): all 61 named tools expose field-level input schemas — pin @zegazone_mcp/mcp@latest so models receive typed parameters instead of a generic args blob.

After a Zegazone API release, update the pinned package in your MCP config and reload the MCP client so new named tools appear. Use thirdparty_call only when no dedicated tool exists yet.

Uploading files

MCP agents and REST clients can upload file bytes directly to Cloudflare R2 using a presigned PUT URL (15-minute expiry). This is separate from playback: stored objects are still served via token-based r2-proxy URLs in the app.

  1. Call media.get_upload_url (or collections.get_upload_url for cover images). MCP tools: media_get_upload_url, collections_get_upload_url.
  2. PUT your file to upload_url with the matching Content-Type header. No Authorization header on the PUT.
  3. Pass the returned r2_key as source_url in media.create, or as thumbnail_url in media.create / media.update / collections.update.
# 1) Presigned upload URL
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"media.get_upload_url","collection_id":123,"filename":"photo.jpg","content_type":"image/jpeg"}'

# 2) PUT file bytes (paste upload_url from step 1)
curl -X PUT -H 'Content-Type: image/jpeg' --data-binary @photo.jpg 'UPLOAD_URL_FROM_STEP_1'

# 3) Create media using r2_key from step 1
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"media.create","collection_id":123,"source_url":"r2://USER_ID/123/TIMESTAMP_photo.jpg","viewer":"image","name":"My Photo"}'

Text notes & carousel icons

Inline text notes are created with media.text_note.add / media.text_note.update (MCP: media_text_note_add, media_text_note_update). Set display_mode to plain, markdown, or code — no file upload required.

For the carousel thumbnail, pass placeholder_kind for a built-in semantic icon, or thumbnail_url for a custom cover. Allowed kinds: log, lyrics, memory, notes, poem, prompt, specification, summary, tutorial. On add, resolution order is thumbnail_url placeholder_kind → default notes. Built-in icons are stored as /assets/player/carousel_placeholders_webp/carousel-placeholder-{kind}.webp (no R2 ingest). On update, omit both thumbnail_url and placeholder_kind to leave the existing thumbnail unchanged.

# Add a text note with a specification carousel icon
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"media.text_note.add","collection_id":123,"name":"API spec","text":"# Requirements\n...","display_mode":"markdown","placeholder_kind":"specification"}'

Tagging

Zegazone uses a curated global vocabulary plus per-user custom shortcuts. Tags are stored lowercase. Collection grid filters use OR logic — matching any selected tag.

Global vocabulary
tags.vocabulary.get — curated slugs with category and sort_order (MCP: tags_vocabulary_get).
Custom shortcuts
profile.get / profile.update available_tags (max 20).
Limits
Collections: max 10 tags. Media: max 10 tags. Profile shortcuts: max 20.
Browse filter
collections.browse accepts optional tags[] (OR) and sort_by (views, recent, likes).

Add Media snapshot:the human Add Media UI copies the parent collection's tags onto each new item by default (snapshot at create time). The API does not auto-copy — when calling media.create / media.upload, omitting tags creates an empty array. Agents should read collections.get and pass tags explicitly to mirror that behaviour.

# List global tag vocabulary
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"tags.vocabulary.get"}'

# Browse own collections tagged photography OR travel (OR filter)
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"collections.browse","scope":["own"],"tags":["photography","travel"],"sort_by":"recent","limit":20}

# Create media with collection-tag snapshot (agent pattern)
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"media.create","collection_id":123,"source_url":"https://example.com/photo.jpg","viewer":"image","tags":["photography","travel"]}'

What can agents do?

Example natural-language prompts you can give Claude, Cursor, or OpenClaw once MCP is connected:

  • What's the description on media item 1936?
  • Find my media tagged 'holiday' across all collections
  • Open the collection at charliefairbairn/boating and list what's in it
  • Create a collection called 'Research 2026' and add these URLs to it
  • Share my boating collection with tom22
  • Show me the most viewed public collections this week

    Displays results live in your Zegazone player — not only in chat.

OAuth setup

  1. Register an OAuth client (or use the bundled MCP client) with PKCE (S256).
  2. Send the user to https://www.zegazone.com/?oauth=1&client_id=…&redirect_uri=…&code_challenge=… with scopes such as collections:read collections:write media:read media:write.
  3. Exchange the authorization code at https://api.zegaphone.com/functions/v1/thirdparty-oauth-token.
  4. Call thirdparty-v1 with Authorization: Bearer <access_token> and your Supabase apikey.

Lookup & search

Prefer single-item reads over listing entire collections. Via MCP, use the matching tool name (snake_case); via REST, send the op directly.

  • media_getmedia.get

    You know a media id and need its metadata or text note.

  • collections_getcollections.get

    You know a collection id and need name, sharing, or stats.

  • collections_get_by_slugcollections.get_by_slug

    You have a public URL handle + slug.

  • media_searchmedia.search

    You have a keyword but not an id (requires query).

  • operations_listoperations.list

    Discover all ops and OAuth scopes.

  • schema_getschema.get

    Full machine-readable contract for an op's fields.

REST examples

# Get one media item by id
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"media.get","media_id":1936}'

# Get one collection by id
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"collections.get","collection_id":123}'

# Search your media by keyword
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"media.search","query":"boating","limit":20}'

# Resolve a public share URL (handle + slug)
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"collections.get_by_slug","handle":"charliefairbairn","slug":"boating"}'

# List your collections
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"collections.list","limit":20}'

# Add a text note with a semantic carousel icon
curl -sS -X POST 'https://api.zegaphone.com/functions/v1/thirdparty-v1' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"op":"media.text_note.add","collection_id":123,"name":"Session log","text":"Day 1 notes","display_mode":"markdown","placeholder_kind":"log"}'

Operations (72)

All requests use POST with an op field. See OpenAPI for field-level schemas.

Meta

  • operation.describeno scope

    Return the schema.request entry for one operation name (from schema.get.operations).

  • operations.listno scope

    List thirdparty-v1 operation names and required OAuth scopes (compact).

  • pingno scope

    Health/auth sanity check for the current access token.

  • schema.getno scope

    Returns this API schema contract for tooling and bots.

Collections

  • collections.archivecollections:write

    Set is_archived=true on an owned collection (hidden from default lists).

  • collections.batch.getcollections:read

    Fetch up to 30 collections by id in one call (owned, non-deleted only). Order matches the request ids where possible.

  • collections.createcollections:write

    Create a new collection owned by the token subject. If thumbnail_url is http(s), the image is downloaded to R2; raster images are processed through the same GCF image pipeline as media uploads (WebP main + thumbnail on the media table, then the collection stores the resulting r2:// thumbnail). When parent_collection_id is set, creates a sub-collection (is_subcollection=true) and inserts the inline subcollection stub media row in the parent collection (requires media:write in addition to collections:write).

  • collections.deletecollections:delete

    Delete an owned collection: soft (is_deleted) by default; hard removes the row (DB cascades media). Non-empty soft-delete requires cascade_media or hard mode.

  • collections.exportcollections:read

    Export one owned collection’s media metadata as JSON or CSV (no binary file bodies; use media.download per item for R2 content).

  • collections.getcollections:read

    Fetch one collection by id when the token subject owns it or is an accepted collaborator.

  • collections.get_by_slugcollections:read

    Resolve a collection from its public URL handle and slug (www.zegazone.com/{handle}/{slug}). Returns the row when the collection is public/registered or the caller has read access.

  • collections.get_upload_urlcollections:write

    Mint a presigned R2 PUT URL for a collection cover image only (purpose=cover). IMPORTANT: The presigned PUT requires direct network access to r2.cloudflarestorage.com — sandboxed agents may not be able to reach R2. After PUT, set collections.update thumbnail_url to the returned r2_key. Do NOT use this for media item thumbnails — use media.get_upload_url with purpose=thumbnail, then media.thumbnail_finalize.

  • collections.listcollections:read

    List collections owned by the token subject. With parent_collection_id, returns only that collection’s sub-collections.

  • collections.publishcollections:write

    Publish an owned collection (share_access_level=public, set share_slug).

  • collections.reordercollections:write

    Assign collection position order by explicit ordered_ids (subset of owned non-deleted collections).

  • collections.restorecollections:write

    Restore an owned soft-deleted collection (is_deleted = false).

  • collections.share_urlcollections:read

    Get the public share URL for a published collection (www.zegazone.com/{handle}/{slug}).

  • collections.unarchivecollections:write

    Set is_archived=false on an owned collection.

  • collections.unpublishcollections:write

    Make an owned collection private.

  • collections.updatecollections:write

    Update one owned collection in place. Only provided fields are changed; optional thumbnail_url can be ingested into R2 like collections.create.

Media

  • media.archivemedia:write

    Set is_archived=true on owned media.

  • media.batch.listmedia:read

    List media across multiple owned collections in one request (max 30 ids). Supports the same limit/offset as media.list on the combined result set.

  • media.copymedia:write

    Duplicate one owned media row into another owned collection (same URLs; new row id).

  • media.createmedia:write

    Create a media row in a collection where the token subject is owner or has upload permission. viewer controls whether URL is treated as website link or ingested to R2. YouTube/Vimeo URLs are always stored as website links (external embed) even if viewer is video. Website items without thumbnail_url get OG image auto-fetched for carousel thumbnail. Tags: max 10 lowercase strings; when omitted, defaults to []. To mirror the human Add Media UI (collection-tag snapshot), read collection.tags via collections.get and pass them explicitly.

  • media.deletemedia:delete

    Delete media in collections where the token subject is owner or has delete permission: hard (row delete, default, matches in-app) or soft (is_deleted). Batch via media_ids.

  • media.describemedia:read

    Return full metadata for one readable media item, including resolved proxy URLs and inferred viewer information. Proxy URLs require Zegazone bearer auth — use media.download_url for a fetchable URL.

  • media.downloadmedia:read

    Download private R2-backed media bytes with OAuth bearer auth.

  • media.download_urlmedia:read

    Mint a short-lived proxied download URL on api.zegaphone.com for a readable media item. Token is embedded in the URL — no bearer auth required for fetch.

  • media.getmedia:read

    Fetch one media row by id when the token subject can read its parent collection.

  • media.get_upload_urlmedia:write

    Mint a presigned R2 PUT URL for uploading file bytes directly (agents/MCP). IMPORTANT: The presigned PUT requires direct network access to r2.cloudflarestorage.com — sandboxed agents may not be able to reach R2. For sandboxed environments, use media.upload instead (accepts base64 content and performs the R2 PUT server-side). After PUT with purpose=thumbnail, set thumbnail_url on the media row then call media.thumbnail_finalize to GCF-resize (200px thumb + 800px background). Do NOT use collections.get_upload_url for media thumbnails.

  • media.listmedia:read

    List media rows in accessible collections with optional filters.

  • media.movemedia:write

    Move one or more owned media rows into another owned collection; optional start position.

  • media.reordermedia:write

    Renumber positions 0..n-1 for all non-deleted media in one owned collection.

  • media.replacemedia:write

    Replace the main content of an owned media item: same ingestion rules as media.create (website = external main_url; otherwise download to R2, raster images run the GCF pipeline). Optional name/type/thumbnail_url updates.

  • media.restoremedia:write

    Restore owned soft-deleted media (is_deleted = false).

  • media.searchmedia:read

    Search accessible media by name, description, and tags (ILIKE + tag overlap). Optional collection_id scopes to one collection.

  • media.text_note.addmedia:write

    Create an inline text-note media row (`text_note` body, `main_url` = inline://text-note). Same storage as app “text note” uploads; no R2 object. Rendering: set `display_mode` (plain|markdown|code) or `viewer` (text|markdown|code); `text` is an alias for plain. If both are sent, they must agree (plain↔text). Set `placeholder_kind` for a semantic carousel icon (defaults to `notes`) or pass `thumbnail_url` for a custom cover.

  • media.text_note.deletemedia:write

    Delete one owned inline text-note media row (`main_url` = inline://text-note). Destructive: requires confirm:true unless dry_run:true (same gate as media.delete).

  • media.text_note.getmedia:read

    Read `text_note` and metadata for one inline text-note media row (`main_url` = inline://text-note). Caller must have read access to the parent collection.

  • media.text_note.updatemedia:write

    Update `text_note` content (and optional display/name/description) for one owned inline text-note media row. Omitted `display_mode` and `viewer` leave type/viewer unchanged. Same display rules as `media.text_note.add`. Update cover with `thumbnail_url` and/or `placeholder_kind`; omit both to leave thumbnail unchanged.

  • media.thumbnail_finalizemedia:write

    After uploading raw image bytes via media.get_upload_url (purpose=thumbnail) and setting thumbnail_url on the media row, call this to GCF-resize to 200px WebP carousel thumb + 800px background in R2 and update thumbnail_url + background_image_url. Also runs automatically when media.create/update sets an r2:// thumbnail key.

  • media.thumbnail_uploadedmedia:write

    Deprecated alias for media.thumbnail_finalize. After PUT bytes to media.get_upload_url (purpose=thumbnail), sets thumbnail_url if provided then runs GCF finalize (200px WebP thumb + 800px background). Prefer media.thumbnail_finalize.

  • media.unarchivemedia:write

    Set is_archived=false on owned media.

  • media.updatemedia:write

    Update fields on media in collections where the token subject is owner or has edit permission. When thumbnail_url is set to an r2:// key from media.get_upload_url, the server auto-runs media.thumbnail_finalize (GCF 200px thumb + 800px background). After uploading via media.get_upload_url, you must call media.thumbnail_finalize if not setting thumbnail_url here. Do NOT use collections.get_upload_url for media thumbnails.

  • media.uploadmedia:write

    Upload a file to Zegazone storage via base64 content (sandbox-friendly alternative to media.get_upload_url). Decodes base64 to original bytes, uploads to R2 with correct Content-Type, auto-detects viewer from MIME type, and creates the media row. Returns the created media item. INTEGRITY: content_length is REQUIRED and verified post-decode — if the decoded size does not match, the upload is rejected (catches truncation). Optionally pass sha256 for full hash verification. ENCODING GUIDANCE: Encode content_base64 IN-PROCESS in your runtime (e.g. Buffer.from(bytes).toString("base64") or btoa) — do NOT shell out to bash and capture the encoded string through a tool-result channel, which is slow for non-trivial files and bloats context. The upload itself is instant; if encoding feels slow, it is your local encode/transport, not the server — do not retry the upload. SWEET SPOT: Best for small files and documents (well under 7.5MB decoded). For large media (video, high-res images), use the presigned flow: media.get_upload_url → HTTP PUT → media.create. Max 10MB base64 (≈7.5MB decoded). Preserves byte-for-byte integrity.

Discovery

  • collections.browseno scope

    Browse collections without a search query. Uses browse_collections_scoped for public/following/liked; own/shared via service-role queries. Scope own/shared need collections:read. Optional tags[] OR-filters by collection tags; sort_by matches browse-collections edge (views, recent, likes).

  • collections.searchno scope

    Search published collections (FTS + trigram). Proxies search_collections_scoped RPC (search-collections edge function). No extra OAuth scope; following/liked scopes need a user-bound token.

  • collections.stats.getcollections:read

    Read like/view stats for a collection the caller can access.

Social

  • aliases.followcollections:write

    Follow another user publish alias (profile_aliases row).

  • aliases.following.listcollections:read

    List aliases and primary usernames the token subject follows. Optional limit/offset paginate (returns a pagination block when either is set).

  • aliases.listcollections:read

    List publish handles (primary username + profile_aliases) for the token subject.

  • aliases.unfollowcollections:write

    Unfollow a publish alias.

  • collections.likecollections:write

    Like a public collection (uses collection_like_set RPC, same as like-collection edge function).

  • collections.liked.listcollections:read

    List collections the token subject has liked.

  • collections.unlikecollections:write

    Remove your like from a collection.

Collaboration

  • collaborators.invitecollections:write

    Invite a Zegazone user to collaborate on a restricted/private owned collection by username. Rate-capped (ZEG-451): 15/hour, max 20 pending per collection, max 50 sent per 24h; breaching returns 429 rate_limited.

  • collaborators.invite.acceptcollections:write

    Accept a pending collaboration invite.

  • collaborators.invite.declinecollections:write

    Decline a pending collaboration invite.

  • collaborators.invites.listcollections:read

    List pending collaboration invites sent by the token subject.

  • collaborators.invites.receivedcollections:read

    List pending collaboration invites received by the token subject.

  • collaborators.listcollections:read

    List accepted collaborators on an owned collection.

  • collaborators.revokecollections:write

    Remove a collaborator or cancel a pending invite on an owned collection.

  • collaborators.update_permissionscollections:write

    Update upload/edit/delete permissions for an existing collaborator or pending invite.

Tags

  • tags.vocabulary.getno scope

    List active global tag vocabulary (slug, label, category, sort_order), ordered by sort_order. Curated list for pickers and agent tagging — distinct from profile custom shortcuts (available_tags, max 20). Collection and media tags are max 10 each.

Profile

  • profile.getno scope

    Get the token subject profile (username, display name, follower counts, bio, aliases, custom tag shortcuts). Response includes available_tags (max 20 lowercase shortcuts for pickers).

  • profile.updateprofile:write

    Update display_name (profiles.full_name), bio (app_settings.about_you), and/or available_tags (max 20 custom shortcuts).

UI

  • ui.state.getcollections:read + media:read

    Return the latest synchronized UI state for the token subject.

  • ui.state.setcollections:read + media:read

    Persist and broadcast the active UI selection, player chrome, and agent browse grid for the token subject.

API

  • bookmarks.createcollections:read

    Save a named bookmark of collection ids for later recall in the player.

  • bookmarks.deletecollections:read

    Delete a bookmark by id.

  • bookmarks.listcollections:read

    List collection browse bookmarks saved by the token subject.

  • mcp.revoke_sessionno scope

    Revoke hosted MCP OAuth sessions for the authenticated user (D1 credentials + oauth_refresh_tokens). Accepts third-party access token or first-party Supabase session JWT from zegazone.com settings.