Skip to content

API Surfaces

The API is versioned under /api/v1. It has five surfaces with different access rules.

Surface Base path Access
Public read API /api/v1/public/* Unauthenticated, read-only for content, rate limited, edge cached
Authentication /api/v1/auth/* Better Auth. Session cookie
Admin API /api/v1/admin/* CMS-role session, allowed Origin, no writes for viewer
System /api/v1/system/* Health, status, first-owner bootstrap, break-glass recovery
Plugin routes /api/plugins/<id>/v1/* and /api/plugins/<id>/public/v1/* Admin routes need a session. Public plugin routes need none

Only the public, authentication and public plugin surfaces are meant for a website. Never expose the admin API to a frontend. The same-origin proxy forwards only /api/v1/auth/*, /api/v1/public/* and /api/plugins/<id>/public/*.

All responses are JSON (except media files). Only published content is returned, and a draft or unknown slug returns the same 404.

Method and path Returns
GET /api/v1/public/{contentType} Published entries of a content type
GET /api/v1/public/{contentType}/{slug} One published entry
GET /api/v1/public/media/{id} Media metadata
GET /api/v1/public/media/{id}/file The file
GET /api/v1/public/media/folders/{slug} Items in a media folder
GET /api/v1/public/global-variables Global variables as one map
GET /api/v1/public/settings/{module} Structured settings: general, contact, social, navigation, footer, seo
GET /api/v1/public/pages Published pages: id, route and title only
GET /api/v1/public/pages/by-route?route=/about One published page with its block tree
GET /api/v1/public/reusable-blocks/{id} A reusable block
GET /api/v1/public/route-patterns { contentTypeSlug, routePattern } pairs
POST /api/v1/public/forms/{slug}/submissions Submit a form (JSON, or multipart for file fields)
POST /api/v1/public/password-reset/request, /confirm Password reset
POST /api/v1/public/recovery/redeem Redeem an Owner recovery code
GET /api/v1/public/preview/{contentType}/{slug}?token=, /pages?route=&token= Signed draft preview
Terminal window
curl https://your-cms-api.example.com/api/v1/public/blog-post/hello-world

Content responses are edge cached for 5 minutes and invalidated when content changes. Everything under /api/v1/public/* shares a 300-per-minute per-IP baseline limit. See Rate limiting.

See Astro authentication. Auth is cookie-session based. There is no bearer-token mode.

/api/v1/admin/* covers dashboard, content types, entries, entry folders, pages, templates, reusable blocks, media, media folders, forms, submissions, global variables, settings, structured settings, email, users, security, webhooks, plugins, cache, and the audit log. It is documented by the deployment’s own OpenAPI document, and is what the admin dashboard itself uses. It is not a stable integration surface for websites.

Each enabled plugin mounts at /api/plugins/<id>/v1/* (session required) and, if it declares them, /api/plugins/<id>/public/v1/* (unauthenticated, same public rate limit). A disabled plugin’s routes return 404. Commerce’s storefront routes are in Commerce.

GET /api/v1/openapi.json and GET /api/v1/docs (Scalar) describe every route that is registered with the OpenAPI layer, including admin and plugin routes’ request and response shapes but never data. They are enabled by default. Set API_DOCS_ENABLED = "false" in [vars] to make both return 404.

GET /api/v1/health returns { status: "ok", version } after a D1 query. GET /api/v1/system/status reports emailConfigured and authSecretConfigured. Both are unauthenticated.

The full route list is in the API routes reference.