{"openapi":"3.1.0","info":{"title":"Exploring Next API","version":"1.0.0","summary":"Read-only public API for the Exploring Next AI podcast.","description":"Episode metadata, audio downloads, structured scripts, and transcripts. No authentication required. Rate limits: 120 requests/minute per IP for metadata routes, 30/minute for audio. The contract is additive-only: fields may be added, never removed or renamed. Human docs: https://sandrise.io/exploring-next/api","termsOfService":"https://sandrise.io/exploring-next/api#usage","contact":{"name":"SandRise","url":"https://sandrise.io/exploring-next"}},"servers":[{"url":"https://ingest.sandrise.io"}],"paths":{"/v1":{"get":{"operationId":"getDiscovery","summary":"API discovery document","responses":{"200":{"description":"Discovery document with endpoint URLs and rate limits."}}}},"/v1/episodes":{"get":{"operationId":"listEpisodes","summary":"List published episodes (newest first, keyset-paginated)","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}},{"name":"cursor","in":"query","description":"Opaque cursor from pagination.nextCursor.","schema":{"type":"string"}},{"name":"domain","in":"query","description":"Filter by domain facet slug (e.g. \"ml\").","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,63}$"}},{"name":"since","in":"query","description":"Only episodes discovered at/after this ISO 8601 date.","schema":{"type":"string"}}],"responses":{"200":{"description":"Page of episodes.","content":{"application/json":{"schema":{"type":"object","required":["episodes","pagination"],"properties":{"episodes":{"type":"array","items":{"$ref":"#/components/schemas/Episode"}},"pagination":{"type":"object","properties":{"nextCursor":{"type":["string","null"],"description":"Pass as ?cursor= to fetch the next page; null when exhausted."},"limit":{"type":"integer"}}}}}}}},"400":{"description":"Invalid input (bad reference, cursor, or filter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Episode not found or not yet published.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Respect Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The episode store or storage upstream is temporarily unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/episodes/{idOrNumber}":{"get":{"operationId":"getEpisode","summary":"Get one episode","parameters":[{"name":"idOrNumber","in":"path","required":true,"description":"Episode UUID or episode number (e.g. 512).","schema":{"type":"string"}}],"responses":{"200":{"description":"Episode resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Episode"}}}},"400":{"description":"Invalid input (bad reference, cursor, or filter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Episode not found or not yet published.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Respect Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The episode store or storage upstream is temporarily unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/episodes/{idOrNumber}/audio":{"get":{"operationId":"getEpisodeAudio","summary":"Stream or download the episode MP3","description":"Supports HTTP Range requests. Add ?download=1 for a Content-Disposition: attachment response with a descriptive filename.","parameters":[{"name":"idOrNumber","in":"path","required":true,"description":"Episode UUID or episode number (e.g. 512).","schema":{"type":"string"}},{"name":"download","in":"query","description":"Set to 1 for an attachment disposition; any other value is treated as inline.","schema":{"type":"string"}}],"responses":{"200":{"description":"Full audio stream (audio/mpeg)."},"206":{"description":"Partial content for Range requests."},"400":{"description":"Invalid input (bad reference, cursor, or filter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Episode not found or not yet published.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Respect Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The episode store or storage upstream is temporarily unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/episodes/{idOrNumber}/script":{"get":{"operationId":"getEpisodeScript","summary":"Structured dialogue script","description":"Speaker-labeled segments with stage directions removed, plus editorial chapters.","parameters":[{"name":"idOrNumber","in":"path","required":true,"description":"Episode UUID or episode number (e.g. 512).","schema":{"type":"string"}}],"responses":{"200":{"description":"Structured script.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Script"}}}},"400":{"description":"Invalid input (bad reference, cursor, or filter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Episode not found or not yet published.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Script exists but could not be parsed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Respect Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The episode store or storage upstream is temporarily unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/episodes/{idOrNumber}/transcript":{"get":{"operationId":"getEpisodeTranscript","summary":"Plain-text transcript","parameters":[{"name":"idOrNumber","in":"path","required":true,"description":"Episode UUID or episode number (e.g. 512).","schema":{"type":"string"}}],"responses":{"200":{"description":"Transcript (text/plain; charset=utf-8)."},"400":{"description":"Invalid input (bad reference, cursor, or filter).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Episode not found or not yet published.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited. Respect Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The episode store or storage upstream is temporarily unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Episode":{"type":"object","required":["id","title","hosts","kind","topics","links"],"properties":{"id":{"type":"string","format":"uuid"},"episodeNumber":{"type":["integer","null"]},"title":{"type":"string"},"shareTitle":{"type":"string"},"summary":{"type":["string","null"]},"description":{"type":["string","null"]},"publishedAt":{"type":["string","null"],"format":"date-time"},"durationSeconds":{"type":["integer","null"]},"sourceUrl":{"type":["string","null"],"format":"uri"},"audioUrl":{"type":"string","format":"uri","description":"Direct MP3 URL on storage. Prefer links.audio for Range support and a descriptive download filename."},"coverImageUrl":{"type":["string","null"],"format":"uri"},"hosts":{"type":"object","required":["a","b"],"properties":{"a":{"type":"string"},"b":{"type":"string"}},"description":"Display names of the two AI hosts for this episode (host a = optimist PM, host b = skeptic engineer)."},"archetype":{"type":["string","null"]},"kind":{"type":"object","properties":{"type":{"type":"string","description":"Episode format. Current values include normal, announcement, overview, and model-behavior; new formats may be added."},"sourceForm":{"type":["string","null"]},"eventType":{"type":["string","null"]}}},"topics":{"type":"object","properties":{"domains":{"type":"array","items":{"type":"string"}},"concepts":{"type":"array","items":{"type":"string"}},"entities":{"type":"array","items":{"type":"string"}}}},"links":{"type":"object","properties":{"self":{"type":"string","format":"uri"},"audio":{"type":"string","format":"uri"},"script":{"type":"string","format":"uri"},"transcript":{"type":"string","format":"uri"},"page":{"type":"string","format":"uri"},"embed":{"type":"string","format":"uri"},"oembed":{"type":"string","format":"uri"}}}}},"Script":{"type":"object","required":["id","title","hosts","segments"],"properties":{"id":{"type":"string","format":"uuid"},"episodeNumber":{"type":["integer","null"]},"title":{"type":"string"},"hosts":{"type":"object","required":["a","b"],"properties":{"a":{"type":"string"},"b":{"type":"string"}},"description":"Display names of the two AI hosts for this episode (host a = optimist PM, host b = skeptic engineer)."},"chapters":{"type":"array","items":{"type":"object","required":["title"],"properties":{"title":{"type":"string"},"summary":{"type":"string"},"takeaways":{"type":"array","items":{"type":"string"}}}}},"segments":{"type":"array","items":{"type":"object","required":["speaker","text"],"properties":{"speaker":{"type":"string","description":"Host display name for this line."},"text":{"type":"string","description":"Spoken line with stage directions removed."}}}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","status"],"properties":{"code":{"type":"string","examples":["not_found","invalid_cursor","rate_limited"]},"message":{"type":"string"},"status":{"type":"integer"}}}}}}}}