{"openapi":"3.1.0","info":{"title":"Subcena HTTP API","version":"1.0.0","summary":"HTTP routes on subcena.com for agents and clients","description":"Public HTTP surface of Subcena. Ticketing data, organizations and realtime state live in Convex; these routes are the Next.js endpoints that agents can call over HTTPS.\n\nAuthentication: GET /api/v1 is public and does not use API keys. Seller dashboards, checkout, wallet generation and POST /api/ai require a Clerk session cookie (__session). There is no public API key to mint.\n\nErrors: JSON object { error: { code, message, hint } }.","contact":{"name":"Subcena","url":"https://subcena.com","email":"contato@subcena.com"}},"servers":[{"url":"https://subcena.com","description":"Production"}],"tags":[{"name":"Public","description":"Unauthenticated read-only discovery. Use this from agents and the CLI."},{"name":"Discovery","description":"Machine-readable indexes"},{"name":"Assistant","description":"Authenticated support assistant"},{"name":"Account","description":"Account and newsletter"},{"name":"Wallet","description":"Apple Wallet and Google Wallet passes"},{"name":"Auth","description":"Native app session handoff"}],"paths":{"/openapi.json":{"get":{"tags":["Discovery"],"summary":"OpenAPI specification","description":"Returns this OpenAPI 3.1 document so agents can bind function calls to Subcena HTTP operations.","operationId":"getOpenApiSpec","responses":{"200":{"description":"OpenAPI 3.1 document","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/llms.txt":{"get":{"tags":["Discovery"],"summary":"llms.txt index","description":"Plain-text index for language models, including when-to-use guidance and links to the public API.","operationId":"getLlmsTxt","responses":{"200":{"description":"Plain-text index for language models","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/sitemap.xml":{"get":{"tags":["Discovery"],"summary":"XML sitemap","description":"Sitemap of indexable public URLs with lastmod dates.","operationId":"getSitemap","responses":{"200":{"description":"Sitemap of public URLs","content":{"application/xml":{"schema":{"type":"string"}}}}}}},"/api":{"get":{"tags":["Discovery"],"summary":"API index","description":"JSON index of machine-readable entry points: OpenAPI, llms.txt, docs and the public v1 API.","operationId":"getApiIndex","responses":{"200":{"description":"JSON index of machine-readable entry points","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiIndex"}}}}}}},"/api/v1":{"get":{"tags":["Public"],"summary":"Public API index","description":"Entry point for the unauthenticated Subcena REST API. Lists public endpoints and explains that admin routes stay gated.","operationId":"getPublicApiIndex","responses":{"200":{"description":"Public API index","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicApiIndex"}}}}}}},"/api/v1/cities":{"get":{"tags":["Public"],"summary":"List supported cities","description":"Returns the cities where Subcena maps public events: Belo Horizonte, Rio de Janeiro and São Paulo.","operationId":"listPublicCities","responses":{"200":{"description":"City list","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/City"}}}}}}}}}},"/api/v1/events":{"get":{"tags":["Public"],"summary":"List public events in a city","description":"Lists map-visible public events for a city. Secret-location events omit street address. Does not require authentication. Use this when a user asks what is happening in BH, Rio or São Paulo.","operationId":"listPublicEvents","parameters":[{"name":"city","in":"query","required":true,"description":"City key. Accepted values: belo_horizonte, rio_de_janeiro, sao_paulo. Aliases: bh, rio, sp.","schema":{"type":"string","enum":["belo_horizonte","rio_de_janeiro","sao_paulo","bh","rio","sp"]}},{"name":"limit","in":"query","required":false,"description":"Maximum number of events to return (1–100). Default 50.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Event list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventList"}}}},"400":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}}}},"/api/v1/events/{id}":{"get":{"tags":["Public"],"summary":"Get a public event","description":"Returns one public event by Convex id or URL slug. Private and cancelled events return 404. Secret addresses are omitted.","operationId":"getPublicEvent","parameters":[{"name":"id","in":"path","required":true,"description":"Event Convex id or slug from the event URL.","schema":{"type":"string"}}],"responses":{"200":{"description":"Event","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/EventDetail"}}}}}},"404":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}}}},"/api/v1/organizations/{id}":{"get":{"tags":["Public"],"summary":"Get a public organization","description":"Returns a public producer, venue or curator page by Convex id or slug. Does not expose emails, passwords or take rates.","operationId":"getPublicOrganization","parameters":[{"name":"id","in":"path","required":true,"description":"Organization Convex id or slug from the profile URL.","schema":{"type":"string"}}],"responses":{"200":{"description":"Organization","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Organization"}}}}}},"404":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}}}},"/api/ai":{"post":{"tags":["Assistant"],"summary":"Ask the Subcena assistant","description":"Authenticated support assistant. Requires a Clerk session cookie. Not part of the public v1 API.","operationId":"postAssistantMessage","security":[{"clerkSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"additionalProperties":false,"properties":{"message":{"type":"string","description":"User message in Portuguese or English"}}}}}},"responses":{"200":{"description":"Assistant reply","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantReply"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"405":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"}}}},"/api/ai/summary":{"post":{"tags":["Assistant"],"summary":"Summarize an organization agenda","description":"Generates a short Portuguese summary of an organization agenda from names and descriptions supplied by the client.","operationId":"postAgendaSummary","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["organizationName","events"],"additionalProperties":false,"properties":{"organizationName":{"type":"string","description":"Display name of the organization"},"events":{"type":"array","description":"Events to summarize","items":{"type":"object","required":["name","description"],"additionalProperties":false,"properties":{"name":{"type":"string"},"description":{"type":"string"}}}}}}}}},"responses":{"200":{"description":"Generated summary","content":{"application/json":{"schema":{"type":"object","required":["summary"],"properties":{"summary":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"}}}},"/api/unsubscribe/org/{token}":{"post":{"tags":["Account"],"summary":"Unsubscribe from an organization newsletter","description":"One-click unsubscribe using the token from a newsletter email. No session required.","operationId":"postOrgNewsletterUnsubscribe","parameters":[{"name":"token","in":"path","required":true,"description":"Unsubscribe token from the newsletter footer.","schema":{"type":"string"}}],"responses":{"200":{"description":"Unsubscribed","content":{"text/plain":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/Error"}}}},"/api/apple-wallet/generate-pass":{"get":{"tags":["Wallet"],"summary":"Generate an Apple Wallet pass for a ticket","description":"Builds a .pkpass for a purchased ticket. Requires ticket metadata from the authenticated ticket page. Not a public discovery endpoint.","operationId":"getAppleWalletPass","parameters":[{"name":"ticketId","in":"query","required":true,"description":"Ticket id","schema":{"type":"string"}},{"name":"eventName","in":"query","required":true,"description":"Event display name printed on the pass","schema":{"type":"string"}},{"name":"eventDateTime","in":"query","required":true,"description":"Event date-time string","schema":{"type":"string"}}],"responses":{"200":{"description":"pkpass file","content":{"application/vnd.apple.pkpass":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"}}}},"/api/google-wallet/generate-link":{"get":{"tags":["Wallet"],"summary":"Generate a Google Wallet save link","description":"Redirects to a Google Wallet save URL for a purchased ticket. Requires pass class identifiers from the ticket page.","operationId":"getGoogleWalletLink","parameters":[{"name":"issuerId","in":"query","required":true,"description":"Google Wallet issuer id","schema":{"type":"string"}},{"name":"classSuffix","in":"query","required":true,"description":"Pass class suffix","schema":{"type":"string"}},{"name":"objectSuffix","in":"query","required":true,"description":"Pass object suffix","schema":{"type":"string"}},{"name":"eventId","in":"query","required":true,"description":"Event id","schema":{"type":"string"}},{"name":"eventName","in":"query","required":true,"description":"Event display name","schema":{"type":"string"}},{"name":"eventDateTime","in":"query","required":true,"description":"Event date-time string","schema":{"type":"string"}}],"responses":{"302":{"description":"Redirect to Google Wallet"},"400":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"}}}},"/api/auth/mobile-ticket":{"get":{"tags":["Auth"],"summary":"Create a one-time Clerk sign-in ticket for the native app","description":"Issues a one-time Clerk sign-in ticket for the Capacitor app. Requires an existing Clerk session. Not public.","operationId":"getMobileTicket","security":[{"clerkSession":[]}],"responses":{"200":{"description":"Sign-in ticket","content":{"application/json":{"schema":{"type":"object","properties":{"ticket":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/Error"}}}},"/api/auth/mobile-handoff":{"post":{"tags":["Auth"],"summary":"Publish a native OAuth handoff ticket","description":"Stores a one-time OAuth handoff ticket for the native app. Requires a Clerk session.","operationId":"postMobileHandoff","security":[{"clerkSession":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["handoffId"],"additionalProperties":false,"properties":{"handoffId":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Handoff stored","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"ticket":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}},"get":{"tags":["Auth"],"summary":"Consume a native OAuth handoff ticket","description":"Consumes a one-time native OAuth handoff ticket identified by handoffId.","operationId":"getMobileHandoff","parameters":[{"name":"handoffId","in":"query","required":true,"description":"Handoff UUID published by the native app","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"One-time ticket","content":{"application/json":{"schema":{"type":"object","properties":{"ticket":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}}},"components":{"securitySchemes":{"clerkSession":{"type":"apiKey","in":"cookie","name":"__session","description":"Clerk session cookie from a signed-in browser or WebView. Not used by GET /api/v1."}},"schemas":{"Error":{"type":"object","required":["error"],"additionalProperties":false,"properties":{"error":{"type":"object","required":["code","message","hint"],"additionalProperties":false,"properties":{"code":{"type":"string","description":"Stable machine code"},"message":{"type":"string","description":"Human-readable error"},"hint":{"type":"string","description":"How to resolve or where to look next"}}}}},"ApiIndex":{"type":"object","required":["name","openapi","llms","docs"],"properties":{"name":{"type":"string"},"openapi":{"type":"string","format":"uri"},"llms":{"type":"string","format":"uri"},"docs":{"type":"string","format":"uri"},"publicApi":{"type":"string","format":"uri"}}},"PublicApiIndex":{"type":"object","required":["name","version","endpoints","auth"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"product":{"type":"string"},"description":{"type":"string"},"documentation":{"type":"string","format":"uri"},"openapi":{"type":"string","format":"uri"},"auth":{"type":"object","properties":{"public":{"type":"string"},"gated":{"type":"string"}}},"endpoints":{"type":"object","additionalProperties":{"type":"string"}}}},"City":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","enum":["belo_horizonte","rio_de_janeiro","sao_paulo"]},"name":{"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"},"radiusKm":{"type":"number"}}},"EventLocation":{"type":"object","nullable":true,"properties":{"name":{"type":["string","null"]},"address":{"type":["string","null"]},"latitude":{"type":["number","null"]},"longitude":{"type":["number","null"]}}},"EventSummary":{"type":"object","required":["id","name","startsAt","url"],"properties":{"id":{"type":"string"},"slug":{"type":["string","null"]},"name":{"type":"string"},"tag":{"type":["string","null"]},"startsAt":{"type":"integer","description":"Unix epoch milliseconds of the event date"},"startTime":{"type":"string"},"endTime":{"type":"string"},"url":{"type":"string","format":"uri"},"city":{"type":["string","null"],"enum":["belo_horizonte","rio_de_janeiro","sao_paulo",null]},"hasSecretAddress":{"type":"boolean"},"location":{"$ref":"#/components/schemas/EventLocation"}}},"EventDetail":{"allOf":[{"$ref":"#/components/schemas/EventSummary"},{"type":"object","properties":{"description":{"type":"string"},"subtitle":{"type":["string","null"]},"salesStatus":{"type":["string","null"]},"organization":{"type":["object","null"],"properties":{"id":{"type":"string"},"slug":{"type":["string","null"]},"name":{"type":"string"},"url":{"type":"string","format":"uri"}}}}}]},"EventList":{"type":"object","required":["city","count","data"],"properties":{"city":{"type":"string","enum":["belo_horizonte","rio_de_janeiro","sao_paulo"]},"count":{"type":"integer"},"data":{"type":"array","items":{"$ref":"#/components/schemas/EventSummary"}}}},"Organization":{"type":"object","required":["id","name","url"],"properties":{"id":{"type":"string"},"slug":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":"string"},"url":{"type":"string","format":"uri"},"city":{"type":["string","null"]},"isCuration":{"type":"boolean"},"isVenue":{"type":"boolean"},"instagram":{"type":["string","null"]}}},"AssistantReply":{"type":"object","properties":{"type":{"type":"string","enum":["text","tickets"]},"text":{"type":"string"}}}},"responses":{"Error":{"description":"Structured JSON error with code, message and a resolution hint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"externalDocs":{"description":"Subcena developer resources and human docs","url":"https://subcena.com/developers"}}