API Reference
Base URL: {APP_URL}/api/v1. Every client — the website, a future desktop app, a future CLI — talks to this exact API; there is no separate internal interface.
Response envelope
Success:
{ "success": true, "data": { }, "meta": { } }
meta is present only for paginated/list endpoints. Error:
{ "success": false, "error": { "code": "PLUGIN_NOT_FOUND", "message": "Plugin not found." } }
error.code is a stable, machine-readable string — branch on it, not on the HTTP status or the message text. A validation error additionally includes error.details, a map of field → array of messages.
Authentication
Bearer JWT access tokens (short-lived, 15 min by default) plus opaque refresh tokens (14 days, stored hashed, rotated on every use).
Authorization: Bearer <access_token>
| Endpoint | Auth | Description |
|---|---|---|
POST /auth/register | — | Create an account, returns a token pair |
POST /auth/login | — | { email, password } → token pair |
POST /auth/refresh | — | { refresh_token } → a new, rotated token pair |
POST /auth/logout | — | Revokes the given refresh token |
POST /auth/forgot-password | — | Always returns success (doesn't leak whether the email exists) |
POST /auth/reset-password | — | { token, password } |
POST /auth/verify-email | — | { token } |
Rate-limited more aggressively than the general API (RATE_LIMIT_AUTH_PER_MINUTE).
Current user
| Endpoint | Auth | Description |
|---|---|---|
GET /me | required | The signed-in user |
PATCH /me | required | Update display_name, bio, website_url, github_url |
POST /me/change-password | required | { current_password, new_password } |
Catalog
| Endpoint | Auth | Description |
|---|---|---|
GET /categories | — | All categories with live plugin counts |
GET /plugins | optional | Browse. Query: category, pricing (free|paid|freemium), min_rating, platform (macos|windows|linux), q, sort (popular|trending|newest|recently_updated|highest_rated), page, per_page |
GET /plugins/search?q= | — | Lightweight typeahead search |
GET /plugins/{slug} | — | Full detail: developer, categories, screenshots, latest version, permissions |
GET /plugins/{slug}/versions | — | Published version history |
GET /plugins/{id}/latest-version | — | Just the newest published version |
GET /plugins/{id}/updates?current_version=1.0.0 | — | { update_available, latest_version } — the desktop update checker |
GET /plugins/{id}/reviews | — | Paginated reviews (with developer replies) |
POST /plugins/{id}/reviews | required | { rating, title?, body? } — upsert (one review per user per plugin) |
POST /plugins/{id}/download | optional | Mints a 5-minute signed download token |
GET /downloads/{token} | — | Streams the .pluginpackage |
POST /reports | required | { reportable_type, reportable_id, reason, details? } |
Library (the signed-in user's collection)
| Endpoint | Auth |
|---|---|
GET /library | required |
POST /library/{pluginId} | required |
PATCH /library/{pluginId} | required — { auto_update: bool } |
DELETE /library/{pluginId} | required |
Future desktop app support
These exist today so a desktop client can be built against a stable contract without another API version bump.
| Endpoint | Auth |
|---|---|
GET /devices / POST /devices / DELETE /devices/{id} | required |
POST /installations | required — reports a local install |
PATCH /installations/{id} | required — updates install status |
Developer API
All under /developer, requiring the developer role (or higher). Becoming a developer: POST /developer/profile (any signed-in user).
| Endpoint | Description |
|---|---|
GET/PATCH /developer/profile | Studio name, support/payout email |
GET /developer/plugins | Every one of your plugins, any status |
POST /developer/plugins | Create a draft plugin |
GET/PATCH /developer/plugins/{id} | |
POST /developer/plugins/{id}/submit | draft/rejected → submitted |
POST /developer/plugins/{id}/icon | /screenshots | multipart image upload |
GET/POST /developer/plugins/{id}/versions | |
POST /developer/plugins/{id}/versions/{versionId}/package | multipart .pluginpackage upload |
POST /developer/plugins/{id}/versions/{versionId}/submit | Requires a package already uploaded |
GET /developer/permissions | The permission catalog to build a submission form against |
GET /developer/analytics | /developer/plugins/{id}/analytics | Installs, downloads, ratings over time |
GET/POST /developer/api-keys, DELETE /developer/api-keys/{id} | Raw key shown once on creation only |
POST /developer/reviews/{reviewId}/reply |
Versioning
The v1 in /api/v1 is a hard contract: existing fields don't change shape or meaning. New optional fields may be added; breaking changes would ship as /api/v2. Semantic versioning of plugin releases is separate and described in docs/plugin-manifest.md.