DocsExtensionsREST API reference
Edit

REST API reference

The Bulwark Extensions directory exposes a small read-mostly REST API that powers both the directory UI and the in-app extension browser inside Bulwark Webmail.

Base URL: https://extensions.bulwarkmail.org/api/v1

All responses are JSON. Authentication is only required for author/admin endpoints (not covered here).

Public endpoints

MethodPathDescription
GET/extensionsList and search extensions with filtering and pagination.
GET/extension/:slugGet full extension details, including latest version and permissions.
GET/extension/:slug/versionsList all published versions of an extension.
GET/extension/:slug/readmeReturn the extension's rendered README (HTML).
GET/extension/:slug/screenshotsList screenshot URLs for an extension.
GET/bundle/:slug/:versionDownload the extension bundle as a ZIP.
POST/check-updatesCheck a list of installed extensions for updates. Used by the admin panel.
GET/extensions/featuredList featured extensions.
GET/extensions/popularList most-downloaded extensions.
GET/extensions/recentList recently-added extensions.
GET/statsGet directory-wide statistics (total extensions, authors, downloads).

GET /extensions accepts the following query parameters:

ParameterTypeDescription
qstringFull-text search across name, description, and tags.
typeplugin | themeFilter by extension type.
pluginTypestringFilter plugins by hook, ui-extension, or sidebar-app.
tagstringFilter by category tag.
sortstringOne of newest, updated, downloads, name. Default: newest.
orderasc | descSort direction. Default depends on sort.
pagenumberPage number (1-indexed). Default: 1.
perPagenumberItems per page (1–100). Default: 24.

Example

curl 'https://extensions.bulwarkmail.org/api/v1/extensions?type=plugin&tag=productivity&sort=downloads'

Response shape:

{
  "data": [
    {
      "slug": "quick-snooze",
      "name": "Quick Snooze",
      "type": "plugin",
      "description": "Snooze emails for later with one keystroke.",
      "iconPath": "abc123/icon.png",
      "totalDownloads": 1842,
      "featured": true,
      "tags": ["productivity"],
      "permissions": ["email:read", "email:write"],
      "license": "MIT",
      "author": {
        "githubLogin": "someone",
        "displayName": "Someone",
        "verified": true
      }
    }
  ],
  "meta": {
    "page": 1,
    "perPage": 24,
    "total": 42
  }
}

Update checks

POST /check-updates takes a JSON body describing the extensions currently installed and returns which ones have a newer version available. The admin panel in Bulwark uses this to surface update badges.

curl -X POST https://extensions.bulwarkmail.org/api/v1/check-updates \
  -H 'Content-Type: application/json' \
  -d '{
    "installed": [
      { "slug": "quick-snooze", "version": "1.0.0" },
      { "slug": "dark-matter", "version": "2.1.3" }
    ]
  }'

Response:

{
  "updates": [
    {
      "slug": "quick-snooze",
      "installedVersion": "1.0.0",
      "latestVersion": "1.2.0",
      "changelog": "https://github.com/someone/quick-snooze/releases/tag/v1.2.0"
    }
  ]
}

Bundles

GET /bundle/:slug/:version returns the extension bundle as a ZIP archive. The response includes Content-Disposition and an ETag tied to the bundle's SHA-256 so downloads are cacheable.

Rate limiting

The public endpoints are served without authentication but may be rate-limited by IP. Long-running integrations should cache responses — the /stats, /extensions/featured, /extensions/popular, and /extensions/recent endpoints are safe to cache for several minutes.