Public API discovery

The public site exposes a small read-only API surface for discovery and health checks. These endpoints are public and do not require OAuth, OIDC, or agent registration.

Discovery resources

  • /.well-known/api-catalog returns the linkset used for automated API discovery.
  • /api/openapi.json publishes an OpenAPI 3.1 description of the public endpoints.
  • /api/health returns a 200 JSON health response for status checks.
  • /auth.md documents that the public site does not currently require authentication.

Endpoints

PathMethodPurpose
/api/v1/healthGETHealth and availability check for automated clients.
/api/v1/og?stack=...GETGenerates the PNG social preview used for stack share links.
/api/health, /api/ogGETUnversioned aliases for the two above. Pin to /api/v1/ for a stable contract.

Errors

Errors are returned as RFC 9457 problem details with the application/problem+json media type. Branch on code, which is stable; title and detail are human-readable and may be reworded. Each error's type URI links to its section below.

Method Not Allowed

405 · method_not_allowed — the endpoint was called with an unsupported HTTP method. Every endpoint on this surface is read-only and accepts only GET and HEAD. The response carries an Allow header listing the accepted methods. Retry with GET.

{
  "type": "https://projects.dev/docs/api/#method-not-allowed",
  "title": "Method Not Allowed",
  "status": 405,
  "code": "method_not_allowed",
  "detail": "This endpoint only supports GET and HEAD. Received POST.",
  "instance": "/api/health"
}

Not Found

404 · not_found — no such endpoint. Unknown paths on this site return a real 404, never a 200 with an app shell, so a path that 404s does not exist. Any unrecognised path under /api/ answers with a problem document rather than an HTML or plaintext error page.

{
  "type": "https://projects.dev/docs/api/#not-found",
  "title": "Not Found",
  "status": 404,
  "code": "not_found",
  "detail": "No such endpoint. This site exposes only GET /api/health and GET /api/og; see /api/openapi.json for the full description.",
  "instance": "/api/does-not-exist"
}

Outside /api/, a request carrying Accept: text/markdown gets a 404 with a markdown recovery body; browsers get the HTML 404 page, whose markdown copy is at /404.md.

Internal Error

500 · internal_error — an unexpected server-side failure. Safe to retry with backoff. Note that /api/og does not use this code: rendering failures there fall back to a generic preview card with a 200, because a broken image in a link unfurl is worse than a generic one.

Authentication and versioning

  • No authentication. The OpenAPI document declares an empty root-level security array and no securitySchemes. No OAuth or OIDC endpoints are published — see /auth.md.
  • /api/v1/ is the versioned surface to pin to. The unversioned paths are aliases that always resolve to the current major, so they may change when v2 ships. A v2 prefix would be added alongside v1, not in place of it.
  • The document itself is versioned by info.version using semantic versioning.
  • Breaking changes are signalled at least 90 days ahead with RFC 9745 Deprecation and RFC 8594 Sunset headers. Full policy on the developer resources page.
  • No rate limiting is applied, so no RateLimit headers are returned. An absent header here means no limit, not an undocumented one. If limits are introduced they will use the RFC 9331 RateLimit headers with Retry-After on 429, announced under the same policy.