{"openapi":"3.1.0","info":{"title":"Cleetus API","version":"1.0.0","summary":"Membership and billing for Cleetus accounts.","contact":{"name":"Cleetus","url":"https://cleetus.dev"}},"externalDocs":{"url":"https://cleetus.dev/api"},"servers":[{"url":"https://api.cleetus.dev"}],"tags":[{"name":"Membership","description":"What the IDE checks."},{"name":"Billing","description":"Stripe checkout and portal."},{"name":"Service","description":"Health and discovery."}],"paths":{"/health":{"get":{"operationId":"getHealth","summary":"Service health","description":"Liveness for monitors and the status badge. Never cached.","tags":["Service"],"security":[],"responses":{"200":{"description":"Service health","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"400":{"description":"Invalid request or signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Memberships are not available yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApi","summary":"This API description","description":"The OpenAPI 3.1 document for the Cleetus API.","tags":["Service"],"security":[],"responses":{"200":{"description":"This API description","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApi"}}}},"400":{"description":"Invalid request or signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Memberships are not available yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/plans":{"get":{"operationId":"getPlan","summary":"Membership plan","description":"Whether memberships are on sale, the monthly price, and the Stripe mode billing runs in.","tags":["Billing"],"security":[],"responses":{"200":{"description":"Membership plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Plan"}}}},"400":{"description":"Invalid request or signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Memberships are not available yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/membership":{"get":{"operationId":"getMembership","summary":"Current membership","description":"The signed-in user's membership. Gate member features on `active`. Only subscriptions in the active Stripe mode count.","tags":["Membership"],"security":[{"cognito":[]}],"responses":{"200":{"description":"Current membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Membership"}}}},"401":{"description":"Missing or invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with the current membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Memberships are not available yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/membership/sync":{"post":{"operationId":"syncMembership","summary":"Refresh from Stripe","description":"Re-reads Stripe and returns the membership. Use after checkout returns; webhooks normally keep it current.","tags":["Membership"],"security":[{"cognito":[]}],"responses":{"200":{"description":"Refresh from Stripe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Membership"}}}},"401":{"description":"Missing or invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with the current membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Memberships are not available yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/membership/checkout":{"post":{"operationId":"createCheckout","summary":"Start checkout","description":"A Stripe Checkout URL for the membership. Returns 409 for existing members and 503 while memberships are closed.","tags":["Billing"],"security":[{"cognito":[]}],"responses":{"200":{"description":"Start checkout","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Redirect"}}}},"401":{"description":"Missing or invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with the current membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Memberships are not available yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/membership/portal":{"post":{"operationId":"createPortal","summary":"Open billing portal","description":"A Stripe billing portal URL for updating payment details, invoices and cancellation.","tags":["Billing"],"security":[{"cognito":[]}],"responses":{"200":{"description":"Open billing portal","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Redirect"}}}},"401":{"description":"Missing or invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with the current membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Memberships are not available yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/stripe/webhook":{"post":{"operationId":"stripeWebhook","summary":"Stripe events","description":"Stripe-signed subscription events. Payloads are never trusted for access; Stripe's current state is re-read.","tags":["Billing"],"security":[],"responses":{"200":{"description":"Stripe events","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Received"}}}},"400":{"description":"Invalid request or signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Memberships are not available yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Health":{"type":"object","required":["ok","service","stage","time"],"properties":{"ok":{"type":"boolean"},"service":{"const":"cleetus-api"},"stage":{"enum":["dev","prod"]},"time":{"type":"string","format":"date-time"}}},"OpenApi":{"type":"object","description":"This document."},"Plan":{"type":"object","required":["available","monthlyCents","currency","interval","mode"],"properties":{"available":{"type":"boolean"},"monthlyCents":{"type":["integer","null"],"examples":[2000]},"currency":{"const":"usd"},"interval":{"const":"month"},"mode":{"enum":["test","live"]}}},"Membership":{"type":"object","required":["userId","active","plan","source","status","currentPeriodEnd","cancelAtPeriodEnd","hasBillingAccount","mode","checkedAt"],"properties":{"userId":{"type":"string","description":"Cognito subject."},"email":{"type":"string","format":"email"},"active":{"type":"boolean","description":"The only field clients should gate on."},"plan":{"enum":["member","free"]},"source":{"enum":["subscription","complimentary",null]},"status":{"type":"string","description":"Stripe subscription status, `complimentary` or `none`.","examples":["active","past_due","canceled","none"]},"currentPeriodEnd":{"type":["integer","null"],"description":"Unix seconds."},"cancelAtPeriodEnd":{"type":"boolean"},"hasBillingAccount":{"type":"boolean"},"mode":{"enum":["test","live"]},"checkedAt":{"type":"string","format":"date-time"}}},"Redirect":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"}}},"Received":{"type":"object","properties":{"received":{"const":true}}},"Error":{"type":"object","required":["message"],"properties":{"message":{"type":"string"}}}},"securitySchemes":{"cognito":{"type":"openIdConnect","openIdConnectUrl":"https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ZzgclTcEp/.well-known/openid-configuration","description":"Authorization code with PKCE. Send the ID or access token as a Bearer token."}}}}