API Changelog
History of changes to the FluffyStack API. New releases at the top.
API version ≠ site version
The version on this page tracks the FluffyStack API contract — endpoints, request shapes, response fields, auth model. It bumps when those change, not when the website does.
The website / app version (shown in the footer and the kiosk overlay) is on a separate track and changes more often as providers, services, and UI features ship. Think Android: the OS version & the API level move independently. Each is what it says on the tin.
API versioning policy
- All current endpoints live under
/v1/. - Backwards-compatible changes (new endpoints, new optional response fields) bump the minor version (1.x) and do NOT break existing consumers.
- Breaking changes would ship under a new
/v2/prefix. The old/v1/stays live indefinitely. - Bookmark this page to stay up to date with API changes.
v1.4.0
Service responses now carry a `lifecycle` block for preview / deprecated / sunset / retired services. Lets integrators flag dependencies on services the vendor has advised migrating off, without scraping the website.
New `lifecycle` field on `Service` and `ServiceDetail` responses. Object with `status` (preview | deprecated | sunset | retired) plus optional `sunsetDate` (ISO YYYY-MM-DD), `gaTarget` (free-form GA window for previews), `successorSlug` (recommended replacement), `note` (vendor migration guidance), and `announcementUrl`. Active GA services omit the field — treat absence as `active`.
GET /v1/servicesGET /v1/services/:slugGET /v1/comparePOST /v1/export/markdownGET /v1/lists/:idOpenAPI spec gains a `ServiceLifecycle` schema and references it from `Service` + `ServiceDetail`. The /v1/openapi.json endpoint reflects this immediately.
D1 migration 0006_lifecycle.sql adds nullable lifecycle_* columns to the `services` table plus a partial index on lifecycle_status. Purely additive — no data loss; existing rows just have NULL lifecycle until the seed re-runs.
v1.3.0
Auth removed entirely. The API is now fully open — no key, no sign-in, no /account page. Mutation endpoints are rate-limited per IP instead of per API key.
All authentication endpoints removed: GET /v1/auth/login/{github,google}, GET /v1/auth/callback/{github,google}, GET /v1/auth/me, POST /v1/auth/rotate, POST /v1/auth/revoke-link, POST /v1/auth/revoke. Any client code that signed in via OAuth or passed an X-API-Key header should drop the header — requests now succeed without it.
GET /v1/auth/login/githubGET /v1/auth/login/googleGET /v1/auth/callback/githubGET /v1/auth/callback/googleGET /v1/auth/mePOST /v1/auth/rotatePOST /v1/auth/revoke-linkPOST /v1/auth/revokeMutation endpoints (POST /v1/policies/*, POST /v1/lists, POST /v1/feedback, POST /v1/export/markdown) no longer require an API key. They're now rate-limited at 10 req/min per IP, down from the previous 30 req/min anonymous bucket — the per-key 120/min path is gone with auth, so the anonymous bucket needs to be tighter.
POST /v1/policies/aws-scpPOST /v1/policies/azure-policyPOST /v1/policies/gcp-org-policyPOST /v1/export/markdownPOST /v1/listsPOST /v1/feedbackRead endpoints unchanged: GETs are still open, still rate-limited only at the Cloudflare edge cache (5-min s-maxage). Cache hits never reach the Worker.
OpenAPI spec at /openapi.json updated: securitySchemes block removed, all 'security: [{ apiKey: [] }]' annotations dropped, UserProfile schema removed (was only used by /v1/auth/me). Existing client generators will produce simpler typed clients on regeneration.
GET /v1/feedback admin-list endpoint and GET /v1/analytics/usage admin endpoint both removed. Both depended on the users table for admin-email lookup. Replace with direct D1 queries — see docs/OPERATIONS.md.
The OAuth-related secrets (GITHUB_CLIENT_SECRET, GOOGLE_CLIENT_SECRET, JWT_SECRET) are no longer needed and can be deleted from the Cloudflare secrets store. The associated GitHub and Google OAuth Apps can also be retired.
v1.2.0
Frontend export gating — all exports require sign-in (free account). Browsing, searching, comparing, and building service lists remain free. (Reverted in 1.3.0.)
All export actions on the website now require authentication. This mirrors the API gating: read-only catalogue access is open, but generating downloadable artefacts (SCP, Azure Policy, GCP Org Policy, Terraform, JSON, Markdown, migration target lists) requires a free account. The Service Builder shows a 'Sign in to export' prompt; the Migration Assistant shows a 'Sign in to export' button.
Account page 'What can you do with an API key?' section rewritten to 'What does signing in unlock?' — now accurately reflects both website and API gating.
v1.1.0
OAuth authentication, API key management, rate limiting, and account management.
GitHub and Google OAuth sign-in. Users authenticate via OAuth and receive a persistent API key (flsk_* format) for all gated endpoints.
GET /v1/auth/login/githubGET /v1/auth/login/googleGET /v1/auth/callback/githubGET /v1/auth/callback/googleAccount management endpoints: view profile, reveal API key, rotate key.
GET /v1/auth/mePOST /v1/auth/rotateKV-based rate limiting. Anonymous: 30 req/min by IP. Authenticated: 120 req/min by API key. X-RateLimit-* response headers on every request.
POST endpoints (policies, export, lists) now require an API key via the X-API-Key header. GET endpoints remain open.
POST /v1/policies/aws-scpPOST /v1/policies/azure-policyPOST /v1/policies/gcp-org-policyPOST /v1/export/markdownPOST /v1/listsCORS policy updated: authenticated requests (with API key) are allowed from any origin. Unauthenticated requests still restricted to fluffystack.dev and *.pages.dev.
API keys are generated using nanoid (cryptographically random, 32 chars). JWT session tokens use HMAC-SHA256 with a 64-byte secret and 7-day expiry.
v1.0.0
Initial public API release. Read-only catalogue, policy generation, service list export, and shareable approved lists.
Read-only catalogue endpoints for providers, categories, and services with pagination, search, and filtering.
GET /v1/providersGET /v1/categoriesGET /v1/servicesGET /v1/services/:slugSide-by-side comparison endpoint for up to 10 services.
GET /v1/compare?ids=a,b,cPolicy generation: AWS SCP, Azure Policy, GCP Org Policy from an approved service list.
POST /v1/policies/aws-scpPOST /v1/policies/azure-policyPOST /v1/policies/gcp-org-policyMarkdown export of approved service lists.
POST /v1/export/markdownShareable approved service lists with 90-day auto-expiry.
POST /v1/listsGET /v1/lists/:idHealth check and status endpoints with history tracking.
GET /v1/healthGET /v1/statusGET /v1/status/historyAWS SCP: removed invalid _comment top-level key that caused AWS Organisations to reject the policy. Provenance now carried in the grammar-valid Id field.
Azure Policy: removed policyType (can't be set), changed mode from "Indexed" to "all" (covers all resource types), removed empty parameters object.
GCP Org Policy: removed invalid _comment top-level key that caused the Resource Manager API to reject the policy.
Missing something? Send us feedback