{
  "openapi": "3.1.0",
  "info": {
    "title": "Chowdahh Agent API",
    "version": "1.0.0",
    "summary": "Curated news for AI agents.",
    "description": "REST API for reading curated news clusters from Chowdahh and recording reader signals. Designed for LLM agents. See https://chowdahh.com/api for narrative docs and https://chowdahh.com/skills/ for prebuilt platform integrations (Claude, ChatGPT, Cursor, MCP, Hermes, OpenClaw).",
    "contact": {
      "name": "Chowdahh",
      "url": "https://chowdahh.com"
    },
    "license": {
      "name": "Read-only public API; submissions and signals require account."
    }
  },
  "servers": [
    {"url": "https://chowdahh.com/api/v1", "description": "Production"}
  ],
  "security": [
    {"bearer_pat": []},
    {"bearer_curator": []},
    {"query_key": []},
    {}
  ],
  "tags": [
    {"name": "Discovery", "description": "List streams, fetch cards, browse topics."},
    {"name": "Signals", "description": "Record reader interactions and feedback."},
    {"name": "Sessions", "description": "Stateful feed sessions with controls."},
    {"name": "Preferences", "description": "Persistent personalization (person token)."},
    {"name": "Submissions", "description": "Submit URLs or collections for curation."},
    {"name": "Radio", "description": "Audio briefings."}
  ],
  "paths": {
    "/streams": {
      "get": {
        "tags": ["Discovery"],
        "summary": "List available streams",
        "operationId": "listStreams",
        "responses": {"200": {"description": "Stream catalog", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/StreamsEnvelope"}}}}}
      }
    },
    "/streams/{slug}": {
      "get": {
        "tags": ["Discovery"],
        "summary": "Get cards from a stream",
        "operationId": "getStream",
        "parameters": [
          {"name": "slug", "in": "path", "required": true, "schema": {"type": "string"}, "description": "One of: top, latest, science, world, tech, business, health, culture, sports, good-news, local."},
          {"name": "limit", "in": "query", "schema": {"type": "integer", "default": 20, "maximum": 50}},
          {"name": "offset", "in": "query", "schema": {"type": "integer", "default": 0}}
        ],
        "responses": {
          "200": {"description": "Cards", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CardsEnvelope"}}}},
          "404": {"description": "Unknown stream slug"}
        }
      }
    },
    "/topics/{topicID}": {
      "get": {
        "tags": ["Discovery"],
        "summary": "Get a topic's fact timeline",
        "operationId": "getTopic",
        "parameters": [
          {"name": "topicID", "in": "path", "required": true, "schema": {"type": "string"}},
          {"name": "limit", "in": "query", "schema": {"type": "integer", "default": 20, "maximum": 100}}
        ],
        "responses": {"200": {"description": "Topic timeline"}}
      }
    },
    "/search": {
      "get": {
        "tags": ["Discovery"],
        "summary": "Search clusters by keyword",
        "operationId": "search",
        "parameters": [
          {"name": "q", "in": "query", "required": true, "schema": {"type": "string"}},
          {"name": "limit", "in": "query", "schema": {"type": "integer", "default": 20, "maximum": 50}}
        ],
        "responses": {"200": {"description": "Search results"}}
      }
    },
    "/curators/{curatorID}": {
      "get": {
        "tags": ["Discovery"],
        "summary": "Get a curator (source) profile",
        "operationId": "getCurator",
        "parameters": [{"name": "curatorID", "in": "path", "required": true, "schema": {"type": "string"}}],
        "responses": {"200": {"description": "Curator profile"}, "404": {"description": "Not found"}}
      }
    },
    "/signals": {
      "post": {
        "tags": ["Signals"],
        "summary": "Record reader signals (batch)",
        "operationId": "recordSignals",
        "security": [{"bearer_pat": []}, {"bearer_curator": []}, {}],
        "requestBody": {
          "required": true,
          "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SignalBatch"}}}
        },
        "responses": {"201": {"description": "Signals accepted"}}
      }
    },
    "/replay": {
      "get": {
        "tags": ["Signals"],
        "summary": "Replay this user's recent signals",
        "operationId": "listReplay",
        "security": [{"bearer_pat": []}, {"query_key": []}],
        "parameters": [
          {"name": "signal_type", "in": "query", "schema": {"type": "string"}},
          {"name": "period", "in": "query", "schema": {"type": "string", "enum": ["today", "last_7_days", "last_30_days"]}},
          {"name": "limit", "in": "query", "schema": {"type": "integer", "default": 50, "maximum": 100}}
        ],
        "responses": {"200": {"description": "Events"}}
      }
    },
    "/stats/activity": {
      "get": {
        "tags": ["Signals"],
        "summary": "Reading activity summary for the authenticated user",
        "operationId": "getActivityStats",
        "security": [{"bearer_pat": []}, {"query_key": []}],
        "responses": {"200": {"description": "Activity rollup"}}
      }
    },
    "/feedback": {
      "post": {
        "tags": ["Signals"],
        "summary": "Submit feedback (content request, bug, feature, quality)",
        "operationId": "submitFeedback",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/FeedbackRequest"}}}},
        "responses": {"201": {"description": "Feedback recorded"}}
      }
    },
    "/feed-sessions": {
      "post": {
        "tags": ["Sessions"],
        "summary": "Create a feed session with controls",
        "operationId": "createFeedSession",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/FeedSessionRequest"}}}},
        "responses": {"201": {"description": "Session created with initial batch"}}
      }
    },
    "/feed-sessions/{sessionID}": {
      "get": {
        "tags": ["Sessions"],
        "summary": "Get current session state",
        "operationId": "getFeedSession",
        "parameters": [{"name": "sessionID", "in": "path", "required": true, "schema": {"type": "string"}}],
        "responses": {"200": {"description": "Session"}, "410": {"description": "Session expired"}}
      }
    },
    "/feed-sessions/{sessionID}/more": {
      "post": {
        "tags": ["Sessions"],
        "summary": "Page the session forward",
        "operationId": "sendMore",
        "parameters": [{"name": "sessionID", "in": "path", "required": true, "schema": {"type": "string"}}],
        "responses": {"200": {"description": "Next batch"}, "410": {"description": "Session expired"}}
      }
    },
    "/feed-sessions/{sessionID}/controls": {
      "patch": {
        "tags": ["Sessions"],
        "summary": "Update controls (filters, rank mode) and get fresh batch",
        "operationId": "updateControls",
        "parameters": [{"name": "sessionID", "in": "path", "required": true, "schema": {"type": "string"}}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/FeedControls"}}}},
        "responses": {"200": {"description": "Updated"}}
      }
    },
    "/preferences/{personID}": {
      "get": {
        "tags": ["Preferences"],
        "summary": "Get persistent preferences",
        "operationId": "getPreferences",
        "security": [{"bearer_pat": []}, {"query_key": []}],
        "parameters": [{"name": "personID", "in": "path", "required": true, "schema": {"type": "string"}}],
        "responses": {"200": {"description": "Preferences"}}
      },
      "put": {
        "tags": ["Preferences"],
        "summary": "Update preferences",
        "operationId": "setPreferences",
        "security": [{"bearer_pat": []}],
        "parameters": [{"name": "personID", "in": "path", "required": true, "schema": {"type": "string"}}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object"}}}},
        "responses": {"200": {"description": "Updated"}}
      }
    },
    "/submissions/items": {
      "post": {
        "tags": ["Submissions"],
        "summary": "Submit a single article URL or object",
        "operationId": "submitItem",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SubmissionItem"}}}},
        "responses": {"201": {"description": "Accepted or already_covered"}}
      }
    },
    "/submissions/collections": {
      "post": {
        "tags": ["Submissions"],
        "summary": "Submit a batch of items",
        "operationId": "submitCollection",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object"}}}},
        "responses": {"201": {"description": "Batch result"}}
      }
    },
    "/submissions/{submissionID}": {
      "get": {
        "tags": ["Submissions"],
        "summary": "Get submission status",
        "operationId": "getSubmission",
        "parameters": [{"name": "submissionID", "in": "path", "required": true, "schema": {"type": "string"}}],
        "responses": {"200": {"description": "Status"}}
      }
    },
    "/radio-sessions": {
      "post": {
        "tags": ["Radio"],
        "summary": "Create an audio briefing session",
        "operationId": "createRadioSession",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RadioRequest"}}}},
        "responses": {"201": {"description": "Tracks with audio_url per track"}}
      }
    },
    "/radio-sessions/{radioSessionID}": {
      "get": {
        "tags": ["Radio"],
        "summary": "Get radio session state",
        "operationId": "getRadioSession",
        "parameters": [{"name": "radioSessionID", "in": "path", "required": true, "schema": {"type": "string"}}],
        "responses": {"200": {"description": "Session"}}
      },
      "patch": {
        "tags": ["Radio"],
        "summary": "Update radio playback position/state",
        "operationId": "updateRadioSession",
        "parameters": [{"name": "radioSessionID", "in": "path", "required": true, "schema": {"type": "string"}}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object"}}}},
        "responses": {"200": {"description": "Updated"}}
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer_pat": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ch_person_* or ohp_pat_*",
        "description": "Person token. 300 requests/min. Get one at https://chowdahh.com/account."
      },
      "bearer_curator": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ch_cur_*",
        "description": "Curator token. 600 requests/min. Issued to registered curators."
      },
      "query_key": {
        "type": "apiKey",
        "in": "query",
        "name": "key",
        "description": "GET-only paste-key auth (ADR-0140). Same tokens as bearer_pat / bearer_curator. Header form is preferred when programmatic."
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": {"description": "Endpoint-specific payload."},
          "guidance": {"$ref": "#/components/schemas/Guidance"},
          "meta": {"$ref": "#/components/schemas/Meta"}
        }
      },
      "Meta": {
        "type": "object",
        "properties": {
          "request_id": {"type": "string"},
          "next_cursor": {"type": "string"},
          "has_more": {"type": "boolean"}
        }
      },
      "Guidance": {
        "type": "object",
        "description": "Per-response instructions for the consuming agent. See https://chowdahh.com/api#guidance.",
        "properties": {
          "status_explanation": {"type": "string"},
          "next_best_actions": {"type": "array", "items": {"$ref": "#/components/schemas/NextBestAction"}},
          "account_state": {"$ref": "#/components/schemas/AccountState"},
          "capability_hints": {"type": "array", "items": {"type": "string"}},
          "suggested_copy": {"type": "array", "items": {"type": "string"}}
        }
      },
      "NextBestAction": {
        "type": "object",
        "properties": {
          "action_id": {"type": "string"},
          "title": {"type": "string"},
          "why": {"type": "string"},
          "kind": {"type": "string", "enum": ["auth_recovery", "quota_recovery", "continue_flow", "personalize", "contribute", "correct", "discover", "upgrade"]},
          "priority": {"type": "string", "enum": ["high", "medium", "low"]},
          "available": {"type": "boolean"},
          "requires_confirmation": {"type": "boolean"},
          "api_hint": {"type": "object", "properties": {"method": {"type": "string"}, "path": {"type": "string"}}},
          "user_facing_prompt": {"type": "string"}
        }
      },
      "AccountState": {
        "type": "object",
        "properties": {
          "auth_mode": {"type": "string", "enum": ["anonymous", "person_token", "curator_token"]},
          "plan_tier": {"type": "string"},
          "rate_limit": {
            "type": "object",
            "properties": {
              "limit": {"type": "integer"},
              "remaining": {"type": "integer"},
              "reset_at": {"type": "string", "format": "date-time"}
            }
          }
        }
      },
      "Card": {
        "type": "object",
        "description": "A curated cluster of corroborating articles. The unit of consumption on Chowdahh — never a raw article.",
        "properties": {
          "id": {"type": "string"},
          "headline": {"type": "string"},
          "summary": {"type": "string"},
          "topics": {"type": "array", "items": {"type": "string"}},
          "significance_score": {"type": "number", "description": "0–100, cross-user. Higher = more newsworthy / more corroboration."},
          "velocity": {"type": "number", "description": "Development growth rate."},
          "decay_percent": {"type": "number"},
          "dev_count": {"type": "integer", "description": "Number of corroborating source developments."},
          "sources": {"type": "array", "items": {"type": "object"}},
          "source_urls": {"type": "array", "items": {"type": "string", "format": "uri"}},
          "share_url": {"type": "string", "format": "uri"},
          "hero_image_url": {"type": "string", "format": "uri"},
          "created_at": {"type": "string", "format": "date-time"},
          "updated_at": {"type": "string", "format": "date-time"}
        }
      },
      "StreamsEnvelope": {"allOf": [{"$ref": "#/components/schemas/Envelope"}, {"type": "object", "properties": {"data": {"type": "object", "properties": {"streams": {"type": "array", "items": {"type": "object"}}, "count": {"type": "integer"}}}}}]},
      "CardsEnvelope": {"allOf": [{"$ref": "#/components/schemas/Envelope"}, {"type": "object", "properties": {"data": {"type": "object", "properties": {"stream": {"type": "string"}, "items": {"type": "array", "items": {"$ref": "#/components/schemas/Card"}}, "count": {"type": "integer"}}}}}]},
      "SignalBatch": {
        "type": "array",
        "maxItems": 100,
        "items": {
          "type": "object",
          "required": ["signal_type", "card_id"],
          "properties": {
            "signal_type": {"type": "string", "enum": ["seen", "open", "save", "share", "dismiss", "source_open", "close", "track", "react", "dwell", "tts_play", "tts_listen"]},
            "card_id": {"type": "string"},
            "variant_id": {"type": "string"},
            "dwell_ms": {"type": "integer"},
            "source_url": {"type": "string"}
          }
        }
      },
      "FeedbackRequest": {
        "type": "object",
        "required": ["feedback_type", "message"],
        "properties": {
          "feedback_type": {"type": "string", "enum": ["content_request", "bug_report", "feature_request", "quality_report"]},
          "message": {"type": "string"},
          "context": {"type": "object"}
        }
      },
      "FeedSessionRequest": {
        "type": "object",
        "properties": {
          "controls": {"$ref": "#/components/schemas/FeedControls"},
          "initial_limit": {"type": "integer", "default": 20, "maximum": 50}
        }
      },
      "FeedControls": {
        "type": "object",
        "properties": {
          "interests": {"type": "array", "items": {"type": "string"}},
          "pin_topics": {"type": "array", "items": {"type": "string"}},
          "block_topics": {"type": "array", "items": {"type": "string"}},
          "rank_mode": {"type": "string", "enum": ["top", "latest"]}
        }
      },
      "SubmissionItem": {
        "type": "object",
        "properties": {
          "title": {"type": "string"},
          "source_url": {"type": "string", "format": "uri"},
          "object_url": {"type": "string", "format": "uri"},
          "summary": {"type": "string"},
          "topics": {"type": "array", "items": {"type": "string"}}
        }
      },
      "RadioRequest": {
        "type": "object",
        "properties": {
          "mode": {"type": "string", "enum": ["headlines", "briefing", "topic_run"]},
          "topics": {"type": "array", "items": {"type": "string"}},
          "max_tracks": {"type": "integer", "default": 5}
        }
      }
    }
  }
}
