{"openapi":"3.1.0","info":{"title":"FluffyStack API","version":"1.4.0","summary":"Programmatic access to the FluffyStack cloud catalogue","description":"Cross-provider cloud-services catalogue with 600+ services across 31 providers, equivalents mapping, compliance tags, and policy generators (AWS SCP, Azure Policy, GCP Org Policy, plus a Markdown exporter for runbooks). v1.4.0 adds an optional `lifecycle` block on Service / ServiceDetail responses for preview / deprecated / sunset / retired services.  **Authentication:** none as of v1.3.0 — no API key, no sign-in. Mutation endpoints are rate-limited per IP (10 requests/min/IP across POST routes); read endpoints aren't rate-limited because they're cached at the Cloudflare edge for 5 minutes.","contact":{"name":"FluffyStack","url":"https://fluffystack.dev/feedback"},"license":{"name":"Open data, free to use with attribution","url":"https://fluffystack.dev/help"}},"servers":[{"url":"https://api.fluffystack.dev","description":"Production"}],"tags":[{"name":"catalogue","description":"Read-only catalogue access"},{"name":"policy","description":"Policy generators (open + rate-limited)"},{"name":"lists","description":"Saved approved-services lists"},{"name":"ops","description":"Health and status"}],"paths":{"/v1/providers":{"get":{"tags":["catalogue"],"summary":"List every cloud provider in the catalogue","responses":{"200":{"description":"Array of providers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Provider"}}}}}}}},"/v1/categories":{"get":{"tags":["catalogue"],"summary":"List service categories (compute, storage, etc.)","responses":{"200":{"description":"Array of categories","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Category"}}}}}}}},"/v1/services":{"get":{"tags":["catalogue"],"summary":"Search and filter cloud services","parameters":[{"name":"provider","in":"query","schema":{"type":"string"},"description":"Provider id, e.g. aws, azure, gcp"},{"name":"category","in":"query","schema":{"type":"string"},"description":"Category id, e.g. compute, storage"},{"name":"q","in":"query","schema":{"type":"string"},"description":"Free-text search across name, description, tags"},{"name":"freeTier","in":"query","schema":{"type":"boolean"},"description":"Limit to services with a free tier"},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Paginated services","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Service"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}}}}},"/v1/services/{slug}":{"get":{"tags":["catalogue"],"summary":"Single-service detail with sub-services + equivalents","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"Service slug, e.g. aws-ec2"}],"responses":{"200":{"description":"Service detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceDetail"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/compare":{"get":{"tags":["catalogue"],"summary":"Side-by-side compare up to 10 services","parameters":[{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated service slugs"}],"responses":{"200":{"description":"Services with shared compare-friendly attributes","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/v1/exposure":{"get":{"tags":["catalogue"],"summary":"Jurisdictional exposure scorecard for a service selection","description":"Returns the same headline numbers the /exposure page renders: provider-HQ jurisdiction breakdown, region-location breakdown, US-HQ percent, sovereign-option count, and European-resident-only count. Includes a 'disclaimer' field — this is procurement triage, not legal advice.","parameters":[{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated service slugs (max 50)"}],"responses":{"200":{"description":"Exposure report","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Missing ids parameter or >50 ids supplied","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/v1/policies/aws-scp":{"post":{"tags":["policy"],"summary":"Generate an AWS Service Control Policy","description":"Returns an SCP scaffold for the approved service ids. Review the actions, add co-required permissions for your environment, and test in a non-prod OU before applying.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovedServicesBody"}}}},"responses":{"200":{"description":"Generated SCP JSON","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/v1/policies/azure-policy":{"post":{"tags":["policy"],"summary":"Generate an Azure Policy initiative","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovedServicesBody"}}}},"responses":{"200":{"description":"Azure Policy JSON","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/v1/policies/gcp-org-policy":{"post":{"tags":["policy"],"summary":"Generate a GCP organisation policy restriction","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovedServicesBody"}}}},"responses":{"200":{"description":"GCP org policy JSON","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/v1/export/markdown":{"post":{"tags":["policy"],"summary":"Render approved services as Markdown (for runbooks)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovedServicesBody"}}}},"responses":{"200":{"description":"Markdown body","content":{"text/markdown":{"schema":{"type":"string"}}}}}}},"/v1/lists":{"post":{"tags":["lists"],"summary":"Save an approved-services list and get a shareable id","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["approvedServiceIds"],"properties":{"name":{"type":"string","description":"Display name"},"approvedServiceIds":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Saved list with a shareable id","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"}}}}}}}}},"/v1/lists/{id}":{"get":{"tags":["lists"],"summary":"Retrieve a saved approved-services list","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Saved list","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/status":{"get":{"tags":["ops"],"summary":"Catalogue status: counts, last updated","responses":{"200":{"description":"Status object","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/v1/status/history":{"get":{"tags":["ops"],"summary":"Status check history (last 24 hours, max 200 rows)","description":"Component-level status records written by the status check job. Useful for monitoring dashboards or post-incident review.","responses":{"200":{"description":"Array of status records ordered most-recent-first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"component":{"type":"string"},"status":{"type":"string","enum":["ok","degraded","down"]},"latencyMs":{"type":"number"},"message":{"type":"string","nullable":true},"timestamp":{"type":"string","format":"date-time"}}}}}}}}}}}},"/v1/health":{"get":{"tags":["ops"],"summary":"Cheap health probe for monitoring tools","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/v1/feedback":{"post":{"tags":["ops"],"summary":"Submit user feedback","description":"Open POST. Rate-limited per IP (10 req/min along with all mutations). Used by the website's /feedback page; can be called directly to integrate FluffyStack feedback into other tooling.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"type":{"type":"string","enum":["bug","feature","question","general"],"description":"Defaults to 'general' if omitted or unrecognised."},"message":{"type":"string","minLength":5,"maxLength":5000,"description":"Body of the feedback. 5–5000 chars."},"email":{"type":"string","format":"email","description":"Optional reply-to address."},"page":{"type":"string","description":"Optional reference to where the feedback was submitted from (e.g. /services/aws-ec2/)."}}}}}},"responses":{"201":{"description":"Feedback recorded","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}}}}},"400":{"description":"Validation error (message too short or too long)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"responses":{"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Provider":{"type":"object","required":["id","name","shortName"],"properties":{"id":{"type":"string","description":"Provider id, e.g. aws"},"name":{"type":"string","description":"Display name"},"shortName":{"type":"string"},"logo":{"type":"string","description":"URL path to provider logo"},"documentationBaseUrl":{"type":"string"},"defaultCompliance":{"type":"array","items":{"type":"string"}}}},"Category":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"icon":{"type":"string"},"description":{"type":"string"}}},"Service":{"type":"object","required":["id","slug","providerId","name","category"],"properties":{"id":{"type":"string"},"slug":{"type":"string"},"providerId":{"type":"string"},"name":{"type":"string"},"shortName":{"type":"string"},"category":{"type":"string"},"description":{"type":"string"},"logo":{"type":"string"},"documentationUrl":{"type":"string"},"pricingUrl":{"type":"string"},"pricingModel":{"type":"string"},"hasFreeTier":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"compliance":{"type":"array","items":{"type":"string"}},"lifecycle":{"$ref":"#/components/schemas/ServiceLifecycle"}}},"ServiceLifecycle":{"type":"object","description":"Optional lifecycle metadata. Present only when the service is in preview, has been deprecated, or has a published sunset/retirement date. Active GA services omit this field entirely — consumers should treat absence as 'active'.","required":["status"],"properties":{"status":{"type":"string","enum":["preview","deprecated","sunset","retired"],"description":"preview = public preview, not yet GA. deprecated = vendor recommends migrating off but service still runs. sunset = retirement date published. retired = past sunset; no new workloads accepted."},"sunsetDate":{"type":"string","format":"date","description":"ISO YYYY-MM-DD. Required for `sunset` and `retired`; ignored for others."},"gaTarget":{"type":"string","description":"Free-form target GA window for `preview` services (e.g. \"2026\" or \"2026-Q3\")."},"successorSlug":{"type":"string","description":"Slug of the recommended successor service. Migration Assistant uses this for one-click swap suggestions."},"note":{"type":"string","description":"Vendor's deprecation rationale or migration guidance."},"announcementUrl":{"type":"string","description":"URL to the vendor's deprecation announcement, for users who want to read the source before believing us."}}},"ServiceDetail":{"allOf":[{"$ref":"#/components/schemas/Service"},{"type":"object","properties":{"subServices":{"type":"array","items":{"type":"object"}},"equivalents":{"type":"array","items":{"$ref":"#/components/schemas/Service"}},"policyIdentifiers":{"type":"object"},"lifecycle":{"$ref":"#/components/schemas/ServiceLifecycle"}}}]},"Pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"}}},"ApprovedServicesBody":{"type":"object","required":["approvedServiceIds"],"properties":{"approvedServiceIds":{"type":"array","items":{"type":"string"},"description":"Service ids from /v1/services"}}},"Error":{"type":"object","required":["error","status"],"properties":{"error":{"type":"string"},"status":{"type":"integer"},"details":{"type":"string"}}}}}}