{
  "openapi": "3.1.0",
  "info": {
    "title": "Diddja API",
    "version": "0.1.0",
    "summary": "Configure a Diddja sports feed and read Barry's archive.",
    "description": "Hand-written against the diddja.config/1 contract. Each operation carries x-diddja-status: 'live' (answers today) or 'not-live' (designed, not built; today the server answers 404). Diddja is sports analysis, not a betting service: no endpoint returns a pick, a side or a bet. Human guide: https://diddja.com/for-agents/",
    "contact": { "url": "https://diddja.com/for-agents/" }
  },
  "servers": [{ "url": "https://diddja.com" }],
  "paths": {
    "/api/signup": {
      "get": {
        "operationId": "signupStatus",
        "summary": "Is signup open? (NOT LIVE)",
        "description": "Side-effect free. Signup is open only when this answers 200 with a SignupStatus whose open is true. Any other answer (today a 404; also any 5xx, or a body without the schema marker) means not open, and /build/ reads it that way.",
        "x-diddja-status": "not-live",
        "responses": {
          "200": { "description": "Signup status.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SignupStatus" } } } },
          "404": { "description": "Signup not built yet.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      },
      "post": {
        "operationId": "signup",
        "summary": "Subscribe one feed (NOT LIVE)",
        "description": "Send one diddja.config/1 document. JSON is the agent path. The /build/ page without JavaScript sends the same fields form-encoded, under the same names (delivery.email for the email). Free text is never accepted into a feed: teams and nets must come from the published lists. Payment is not taken by this endpoint.",
        "x-diddja-status": "not-live",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/Config" },
              "example": {
                "schema": "diddja.config/1",
                "sport": "nfl",
                "teams": ["GB", "CHI", "DET"],
                "nets": ["nfl.injury.qb", "nfl.coaching.seat"],
                "voice": "barry",
                "formats": ["newsletter", "api"],
                "cadence": "daily",
                "delivery": { "email": "you@example.com" }
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": { "$ref": "#/components/schemas/ConfigForm" },
              "encoding": {
                "teams": { "style": "form", "explode": true },
                "nets": { "style": "form", "explode": true },
                "formats": { "style": "form", "explode": true }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Feed created. The response shape is set by the signup build and is not fixed here." },
          "400": { "description": "The config failed validation.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "Today's answer: signup not built yet.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/nets": {
      "get": {
        "operationId": "listNets",
        "summary": "The beat catalogue (NOT LIVE)",
        "description": "Every net/1 a config may name. Until this is live, the five launch nets are listed in the Config schema's enum below and on https://diddja.com/build/.",
        "x-diddja-status": "not-live",
        "parameters": [
          { "name": "sport", "in": "query", "schema": { "type": "string", "example": "nfl" } }
        ],
        "responses": {
          "200": { "description": "The catalogue.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Net" } } } } },
          "404": { "description": "Today's answer: not built yet." }
        }
      }
    },
    "/api/articles": {
      "get": {
        "operationId": "searchArticles",
        "summary": "Search Barry's articles",
        "description": "What the chat's retrieval returns for a query: ids, headlines, dates, links, scores. Reads only the public index; makes no model call.",
        "x-diddja-status": "live",
        "parameters": [
          { "name": "q", "in": "query", "required": true, "schema": { "type": "string", "maxLength": 500 }, "example": "Packers quarterback" }
        ],
        "responses": {
          "200": {
            "description": "Up to four hits.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "prompt_version": { "type": "string" },
                    "index_generated_at": { "type": "string" },
                    "hits": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "article_id": { "type": "string" },
                          "headline": { "type": "string" },
                          "date": { "type": "string" },
                          "url": { "type": ["string", "null"], "description": "Site-relative, e.g. /a/<slug>/" },
                          "score": { "type": "number" }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "405": { "description": "GET only." }
        }
      }
    },
    "/archive.json": {
      "get": {
        "operationId": "archive",
        "summary": "The article archive index (static file)",
        "x-diddja-status": "live",
        "responses": { "200": { "description": "The archive.", "content": { "application/json": {} } } }
      }
    },
    "/api/v1/stoa/stalls/b3dfaab7-e880-4189-97f1-b80b35c97e6f/content": {
      "servers": [{ "url": "https://aexgora.onrender.com", "description": "The AEX board, where Diddja holds a stall." }],
      "get": {
        "operationId": "stallContent",
        "summary": "Diddja's stall items on the AEX board",
        "x-diddja-status": "live",
        "parameters": [
          { "name": "sport", "in": "query", "schema": { "type": "string" }, "example": "nfl" },
          { "name": "tag", "in": "query", "schema": { "type": "string" } },
          { "name": "facets", "in": "query", "schema": { "type": "string", "enum": ["1"] }, "description": "List the sport and tag values in use." }
        ],
        "responses": { "200": { "description": "Stall items.", "content": { "application/json": {} } } }
      }
    }
  },
  "components": {
    "schemas": {
      "Config": {
        "title": "diddja.config/1",
        "description": "One subscriber's or agent's feed.",
        "type": "object",
        "required": ["schema", "sport", "teams", "voice", "formats", "cadence", "delivery"],
        "additionalProperties": false,
        "properties": {
          "schema": { "const": "diddja.config/1" },
          "sport": { "type": "string", "enum": ["nfl"], "description": "NFL only at launch; the other sports Barry covers follow." },
          "teams": {
            "type": "array", "minItems": 1, "maxItems": 5, "uniqueItems": true,
            "items": { "$ref": "#/components/schemas/NflTeam" }
          },
          "nets": {
            "type": "array", "minItems": 0, "maxItems": 5, "uniqueItems": true,
            "description": "Beat ids from the catalogue. The five launch nets are listed; the catalogue is canonical once /api/nets is live.",
            "items": { "type": "string", "enum": ["nfl.injury.qb", "nfl.injury.any", "nfl.depth.change", "nfl.coaching.seat", "nfl.schedule.spot"] }
          },
          "voice": { "type": "string", "enum": ["barry"] },
          "formats": {
            "type": "array", "minItems": 1, "uniqueItems": true,
            "items": { "type": "string", "enum": ["newsletter", "api", "articles"] }
          },
          "cadence": { "type": "string", "enum": ["daily"] },
          "delivery": {
            "description": "Email or agent delivery. Webhook delivery is not offered.",
            "oneOf": [
              { "type": "object", "required": ["email"], "additionalProperties": false, "properties": { "email": { "type": "string", "format": "email" } } },
              { "type": "object", "required": ["agent"], "additionalProperties": false, "properties": { "agent": { "const": true } } }
            ]
          }
        }
      },
      "ConfigForm": {
        "description": "The same config as the /build/ form sends it without JavaScript. Repeated keys carry the arrays.",
        "type": "object",
        "properties": {
          "schema": { "const": "diddja.config/1" },
          "sport": { "type": "string" },
          "teams": { "type": "array", "items": { "type": "string" } },
          "nets": { "type": "array", "items": { "type": "string" } },
          "voice": { "type": "string" },
          "formats": { "type": "array", "items": { "type": "string" } },
          "cadence": { "type": "string" },
          "delivery.email": { "type": "string", "format": "email" }
        }
      },
      "NflTeam": {
        "type": "string",
        "enum": ["BUF", "MIA", "NE", "NYJ", "BAL", "CIN", "CLE", "PIT", "HOU", "IND", "JAX", "TEN", "DEN", "KC", "LV", "LAC",
                 "DAL", "NYG", "PHI", "WSH", "CHI", "DET", "GB", "MIN", "ATL", "CAR", "NO", "TB", "ARI", "LAR", "SF", "SEA"]
      },
      "Net": {
        "title": "diddja.net/1",
        "description": "A catalogue entry: a fixed set of filters. Free text never enters a net.",
        "type": "object",
        "required": ["schema", "net_id", "label", "sport", "filters", "version"],
        "properties": {
          "schema": { "const": "diddja.net/1" },
          "net_id": { "type": "string", "example": "nfl.injury.qb" },
          "label": { "type": "string", "example": "QB injuries" },
          "sport": { "type": "string" },
          "filters": {
            "type": "object",
            "properties": {
              "topic": { "type": ["string", "null"] },
              "position": { "type": ["string", "null"] },
              "conference": { "type": ["string", "null"] },
              "division": { "type": ["string", "null"] },
              "team": { "type": ["string", "null"] }
            }
          },
          "catches": { "type": "string" },
          "version": { "type": "integer" }
        }
      },
      "Story": {
        "title": "diddja.story/1",
        "description": "What a net catches, as served. Ergon ids may be listed in record_ids; ergon bodies and verbatim quotes are never served. Field shapes below are as far as the contract fixes them.",
        "type": "object",
        "required": ["story_id", "net_id", "teams", "headline", "reasons", "links", "sourcing", "source_tier", "first_seen", "record_ids"],
        "properties": {
          "story_id": { "type": "string" },
          "net_id": { "type": "string" },
          "teams": { "type": "array", "items": { "type": "string" } },
          "headline": { "type": "string" },
          "reasons": { "type": "array", "items": { "type": "object", "properties": { "code": { "type": "string" }, "text": { "type": "string" } } } },
          "links": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string", "format": "uri" }, "title": { "type": "string" } } }, "description": "Links to the public sources." },
          "sourcing": { "type": "object", "description": "Counts distinct publishers per side, not erga (one vote per independent source)." },
          "source_tier": { "type": "string" },
          "first_seen": { "type": "string", "format": "date-time" },
          "record_ids": { "type": "array", "items": { "type": "string" }, "description": "Ids of the sourced facts behind the story." }
        }
      },
      "SignupStatus": {
        "title": "diddja.signup-status/1",
        "type": "object",
        "required": ["schema", "open"],
        "properties": {
          "schema": { "const": "diddja.signup-status/1" },
          "open": { "type": "boolean" }
        }
      },
      "Error": {
        "type": "object",
        "properties": { "error": { "type": "string" } }
      }
    }
  }
}
