{"components":{"schemas":{"AppError":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["https://example.com/AppError.json"],"format":"uri","readOnly":true,"type":"string"},"detail":{"type":"string"},"errors":{"items":{"$ref":"#/components/schemas/ErrorDetail"},"type":["array","null"]},"message":{"type":"string"},"status":{"format":"int64","type":"integer"},"title":{"type":"string"}},"required":["status","title","detail","message"],"type":"object"},"Detail":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["https://example.com/Detail.json"],"format":"uri","readOnly":true,"type":"string"},"created_at":{"format":"date-time","type":"string"},"episode":{"format":"int64","type":"integer"},"pipeline_version":{"format":"int64","type":"integer"},"processed_at":{"format":"date-time","type":"string"},"show":{"type":"string"},"title":{"type":"string"},"transcript":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"word_count":{"format":"int64","type":"integer"}},"required":["show","episode","transcript","created_at","updated_at"],"type":"object"},"ErrorDetail":{"additionalProperties":false,"properties":{"location":{"type":"string"},"message":{"type":"string"},"value":{}},"type":"object"},"GetPostsResponseBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["https://example.com/GetPostsResponseBody.json"],"format":"uri","readOnly":true,"type":"string"},"has_more":{"description":"Whether more items are available","type":"boolean"},"items":{"description":"Feed items","items":{"$ref":"#/components/schemas/Post"},"type":["array","null"]},"next_cursor":{"description":"Cursor for the next page; empty when has_more is false","type":"string"},"page":{"description":"Current page (offset pagination only)","format":"int64","type":"integer"},"total":{"description":"Total matching posts (offset pagination only)","format":"int64","type":"integer"},"total_pages":{"description":"Total pages at this limit (offset pagination only)","format":"int64","type":"integer"}},"required":["items","has_more"],"type":"object"},"ListTranscriptsResponseBody":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["https://example.com/ListTranscriptsResponseBody.json"],"format":"uri","readOnly":true,"type":"string"},"has_more":{"description":"Whether more items are available","type":"boolean"},"items":{"description":"Transcript summaries (full text only on the detail endpoint)","items":{"$ref":"#/components/schemas/Summary"},"type":["array","null"]},"page":{"description":"Current page","format":"int64","type":"integer"}},"required":["items","has_more","page"],"type":"object"},"Post":{"additionalProperties":false,"properties":{"author":{"type":"string"},"excerpt":{"type":"string"},"featured":{"type":"boolean"},"id":{"type":"string"},"image_url":{"type":"string"},"published_at":{"format":"date-time","type":"string"},"reading_time":{"format":"int64","type":"integer"},"slug":{"type":"string"},"tags":{"items":{"type":"string"},"type":["array","null"]},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"visibility":{"type":"string"}},"required":["id","title","published_at","updated_at","slug","tags","featured"],"type":"object"},"PostDetail":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["https://example.com/PostDetail.json"],"format":"uri","readOnly":true,"type":"string"},"author":{"type":"string"},"excerpt":{"type":"string"},"featured":{"type":"boolean"},"id":{"type":"string"},"image_url":{"type":"string"},"lexical":{},"plaintext":{"type":"string"},"published_at":{"format":"date-time","type":"string"},"reading_time":{"format":"int64","type":"integer"},"seo":{"$ref":"#/components/schemas/PostSEO"},"slug":{"type":"string"},"source_id":{"type":"string"},"tags":{"items":{"type":"string"},"type":["array","null"]},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"visibility":{"type":"string"}},"required":["id","title","published_at","updated_at","source_id","slug","tags","featured"],"type":"object"},"PostSEO":{"additionalProperties":false,"properties":{"canonical_url":{"type":"string"},"meta_description":{"type":"string"},"meta_title":{"type":"string"},"og_description":{"type":"string"},"og_image":{"type":"string"},"og_title":{"type":"string"},"twitter_description":{"type":"string"},"twitter_image":{"type":"string"},"twitter_title":{"type":"string"}},"type":"object"},"Summary":{"additionalProperties":false,"properties":{"episode":{"format":"int64","type":"integer"},"processed_at":{"format":"date-time","type":"string"},"rank":{"description":"Search relevance (search results only)","format":"double","type":"number"},"show":{"type":"string"},"snippet":{"description":"Highlighted match fragment with \u003cmark\u003e tags (search results only)","type":"string"},"title":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"word_count":{"format":"int64","type":"integer"}},"required":["show","episode","updated_at"],"type":"object"},"Tag":{"additionalProperties":false,"properties":{"count":{"format":"int64","type":"integer"},"description":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["slug","name","count"],"type":"object"},"TagsResponse":{"additionalProperties":false,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["https://example.com/TagsResponse.json"],"format":"uri","readOnly":true,"type":"string"},"tags":{"items":{"$ref":"#/components/schemas/Tag"},"type":["array","null"]}},"required":["tags"],"type":"object"}},"securitySchemes":{"apiKeyAuth":{"description":"Member API key; grants the owning member's access on routes that opt in","in":"header","name":"X-API-Key","type":"apiKey"}}},"info":{"description":"Programmatic, read-only access to Turing Station posts — the same content the website serves, over a small JSON API. Requests without credentials see publicly visible content; an API key unlocks whatever your membership includes.\n\n# Getting started\n\n1. Sign in on the Turing Station website and open **Settings → API keys**.\n2. Create a key with a name that describes what will use it (e.g. \"research agent\"). The key is shown **once**, at creation — store it somewhere safe. You can keep up to 15 active keys and revoke any of them later.\n3. Make your first request:\n\n```bash\ncurl \"https://api.turingstation.nl/content/posts?limit=5\" \\\n  -H \"X-API-Key: \u003cyour key\u003e\"\n```\n\n\n# Command line\n\n[`tstation`](https://github.com/Bitcoin-Alpha/tstation) is the official CLI for this API. Try it without installing:\n\n```bash\nnpx @label21/tstation posts list\n```\n\nOr install it globally to get the `tstation` command:\n\n```bash\nnpm install -g @label21/tstation\n```\n\nCommands:\n\n- `tstation posts list` — browse the feed, with `--tags`, `--search`, and `--featured` filters\n- `tstation posts get \u003cid-or-slug\u003e` — read a post in your terminal\n- `tstation posts tags` — list tags\n- `tstation auth login` / `auth status` / `auth logout` — store, inspect, or remove your API key\n\nAdd `--json` to any command for raw output that pipes into `jq`. In scripts and CI, the `TSTATION_API_KEY` and `TSTATION_API_URL` environment variables override the stored configuration.\n\n# MCP\n\nAI assistants can use this API through the Model Context Protocol endpoint at `https://api.turingstation.nl/content/mcp`. It exposes five tools mirroring the REST surface:\n\n- `list_posts` — compact post summaries, paginated, with tag, search, and featured filters\n- `get_post` — a single post as plain text, by id or slug\n- `list_tags` — all tags with post counts\n- `list_transcripts` — podcast episode transcript summaries, with full-text search and ranked, highlighted snippets (requires an API key)\n- `get_transcript` — one episode's transcript in paged windows (`offset`/`max_words`, follow `next_offset`), by show and episode number (requires an API key)\n\nAuthentication works exactly as for REST: the `X-API-Key` header is optional, anonymous access sees public content, and your key unlocks your membership's tiers. The transport is stateless Streamable HTTP, so any MCP client that can send a custom header works — no session management required. For Claude Code:\n\n```bash\nclaude mcp add --transport http turingstation https://api.turingstation.nl/content/mcp --header \"X-API-Key: \u003cyour key\u003e\"\n```\n\nIn Claude Desktop, add a custom connector with the same URL and header.","title":"Turing Station API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/content/posts":{"get":{"description":"Keyset-paginated list of posts. Supports optional authentication for personalized content, reading progress, and access control. When authenticated, includes reading_progress with scroll percentage and read status for each post.","operationId":"list-posts","parameters":[{"description":"Opaque pagination cursor from a prior response's next_cursor; empty for the first page","explode":false,"in":"query","name":"cursor","schema":{"description":"Opaque pagination cursor from a prior response's next_cursor; empty for the first page","type":"string"}},{"description":"1-based page for offset pagination with totals; mutually exclusive with cursor (cursor wins when both are sent)","explode":false,"in":"query","name":"page","schema":{"description":"1-based page for offset pagination with totals; mutually exclusive with cursor (cursor wins when both are sent)","format":"int64","maximum":100000,"minimum":1,"type":"integer"}},{"description":"Items per page","explode":false,"in":"query","name":"limit","schema":{"default":15,"description":"Items per page","format":"int64","maximum":50,"minimum":1,"type":"integer"}},{"description":"Comma-separated tags to filter by","explode":false,"in":"query","name":"tags","schema":{"description":"Comma-separated tags to filter by","type":"string"}},{"description":"Search query","explode":false,"in":"query","name":"search","schema":{"description":"Search query","type":"string"}},{"description":"Filter by read status (all, read, unread)","explode":false,"in":"query","name":"read_status","schema":{"default":"all","description":"Filter by read status (all, read, unread)","enum":["all","read","unread"],"type":"string"}},{"description":"Filter by featured flag; omit for all posts","explode":false,"in":"query","name":"featured","schema":{"description":"Filter by featured flag; omit for all posts","enum":["true","false"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetPostsResponseBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppError"}}},"description":"Error"}},"security":[{},{"apiKeyAuth":[]}],"summary":"List posts","tags":["Posts"],"x-public":true}},"/content/posts/tags":{"get":{"description":"Get all available post tags for filtering.","operationId":"get-post-tags","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagsResponse"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppError"}}},"description":"Error"}},"summary":"Get available tags","tags":["Posts"],"x-public":true}},"/content/posts/{id}":{"get":{"description":"Get a single post. The formats query param selects the content representation: plaintext (default) and/or lexical, comma-separated. The path parameter accepts either a post UUID or a slug (SEO-friendly URLs). Access control is applied based on the user's subscription tier. When authenticated, includes reading_progress and is_bookmarked fields.","operationId":"get-post","parameters":[{"description":"Feed item ID","in":"path","name":"id","required":true,"schema":{"description":"Feed item ID","type":"string"}},{"description":"Comma-separated content formats to include: plaintext (default) and/or lexical","example":"plaintext,lexical","explode":false,"in":"query","name":"formats","schema":{"default":"plaintext","description":"Comma-separated content formats to include: plaintext (default) and/or lexical","examples":["plaintext,lexical"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostDetail"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppError"}}},"description":"Error"}},"security":[{},{"apiKeyAuth":[]}],"summary":"Get post detail","tags":["Posts"],"x-public":true}},"/content/transcripts":{"get":{"description":"Page through transcript summaries, or full-text search them with the search param (ranked, with a highlighted snippet per hit). Requires a member JWT or API key; the full transcript text is only served by the detail endpoint.","operationId":"list-transcripts","parameters":[{"description":"Full-text search query; when set, results are ranked and carry a snippet","explode":false,"in":"query","name":"search","schema":{"description":"Full-text search query; when set, results are ranked and carry a snippet","type":"string"}},{"description":"Filter by show slug","explode":false,"in":"query","name":"show","schema":{"description":"Filter by show slug","type":"string"}},{"description":"1-based page","explode":false,"in":"query","name":"page","schema":{"default":1,"description":"1-based page","format":"int64","maximum":100000,"minimum":1,"type":"integer"}},{"description":"Items per page","explode":false,"in":"query","name":"limit","schema":{"default":15,"description":"Items per page","format":"int64","maximum":50,"minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListTranscriptsResponseBody"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppError"}}},"description":"Error"}},"security":[{"apiKeyAuth":[]}],"summary":"List or search transcripts","tags":["Transcripts"],"x-public":true}},"/content/transcripts/{show}/{episode}":{"get":{"description":"Full transcript for one episode, including the inline [HH:MM:SS] speaker markers. Requires a member JWT or API key.","operationId":"get-transcript","parameters":[{"description":"Show slug","in":"path","name":"show","required":true,"schema":{"description":"Show slug","maxLength":100,"type":"string"}},{"description":"Episode number","in":"path","name":"episode","required":true,"schema":{"description":"Episode number","format":"int64","minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Detail"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppError"}}},"description":"Error"}},"security":[{"apiKeyAuth":[]}],"summary":"Get a transcript","tags":["Transcripts"],"x-public":true}}},"servers":[{"url":"https://api.turingstation.nl"}]}