{
  "openapi": "3.1.0",
  "info": {
    "title": "Luke F. Walton's archive: retrieval-only search",
    "version": "archive-search/1",
    "summary": "Ranked hits with provenance over a personal archive. Nothing is synthesized; the caller brings its own model.",
    "description": "Semantic search over the public and private material of Luke F. Walton (songs, essays, papers, podcast episodes and their transcripts, pages). Every hit carries provenance (creators with identifiers, date, version, URL, catalogue identifiers, a locator) and an `exposure` that says what may be known about it: The passage is public and is included verbatim. Quote it with its entity.url. The source is not quotable here. gist is a description of what the passage is about, drafted by the archive's software at ingest and released under the archive owner's policy; gistSource says whether a person edited it and gistReview whether a person reviewed it. It is not a quotation and not the creators' wording. Only the location of relevant material is released. Do not infer its contents. Nothing in this response was synthesized at request time. Scores on private hits are rounded. Attribution entries with a placeholder are not people: unnamed is a speaker who is not a creator, unverified is attribution not established. Undated material is excluded when a date bound is given unless undated=include. Cite a hit by its entity.url. Query text is not logged by default. Limits: 20 hits per request, 30 requests per minute per caller; run several narrow searches rather than one wide one.",
    "contact": {
      "name": "Luke F. Walton",
      "url": "https://lukefwalton.com/"
    }
  },
  "externalDocs": {
    "description": "What each exposure means, how to cite, and the MCP endpoint.",
    "url": "https://lukefwalton.com/ask/api/"
  },
  "servers": [
    {
      "url": "https://lukefwalton.com"
    }
  ],
  "paths": {
    "/api/archive/search": {
      "get": {
        "operationId": "searchArchive",
        "summary": "Search the archive; returns ranked hits, never an answer.",
        "description": "The query is embedded once server-side and scored against every served fragment. Identical requests are served by the CDN for a day, so repeat a URL freely.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "The question or phrase to search for (1–500 characters). Embedded once, server-side; never logged by default.",
            "schema": {
              "type": "string",
              "description": "The question or phrase to search for (1–500 characters). Embedded once, server-side; never logged by default.",
              "minLength": 1,
              "maxLength": 500,
              "examples": [
                "perfect pitch nature or nurture"
              ]
            },
            "example": "perfect pitch nature or nurture"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Total hits to return across exposures, after filters and the score floor. Default 10; at most 20. Run several narrow searches rather than one wide one.",
            "schema": {
              "type": "integer",
              "description": "Total hits to return across exposures, after filters and the score floor. Default 10; at most 20. Run several narrow searches rather than one wide one.",
              "minimum": 1,
              "maximum": 20,
              "default": 10
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Entity types to keep (song, album, writing, letter, episode, transcript, publication, interview, page), comma-separated.",
            "schema": {
              "type": "string",
              "description": "Entity types to keep (song, album, writing, letter, episode, transcript, publication, interview, page), comma-separated.",
              "examples": [
                "song,album"
              ]
            },
            "example": "song,album"
          },
          {
            "name": "date_from",
            "in": "query",
            "required": false,
            "description": "Keep fragments whose date interval intersects [date_from, date_to]. YYYY, YYYY-MM, or YYYY-MM-DD; a bare year means the whole year.",
            "schema": {
              "type": "string",
              "description": "Keep fragments whose date interval intersects [date_from, date_to]. YYYY, YYYY-MM, or YYYY-MM-DD; a bare year means the whole year.",
              "pattern": "^\\d{4}(-\\d{2}(-\\d{2})?)?$",
              "examples": [
                "2022"
              ]
            },
            "example": "2022"
          },
          {
            "name": "date_to",
            "in": "query",
            "required": false,
            "description": "Upper bound of the date window (inclusive, at its precision).",
            "schema": {
              "type": "string",
              "description": "Upper bound of the date window (inclusive, at its precision).",
              "pattern": "^\\d{4}(-\\d{2}(-\\d{2})?)?$",
              "examples": [
                "2023-06"
              ]
            },
            "example": "2023-06"
          },
          {
            "name": "undated",
            "in": "query",
            "required": false,
            "description": "What to do with undated material when a date bound is set. Default 'exclude'; the response counts what was excluded.",
            "schema": {
              "type": "string",
              "description": "What to do with undated material when a date bound is set. Default 'exclude'; the response counts what was excluded.",
              "enum": [
                "include",
                "exclude"
              ],
              "default": "exclude"
            }
          },
          {
            "name": "creator",
            "in": "query",
            "required": false,
            "description": "Keep entities whose creators include this name (case-insensitive phrase match). Placeholders never match.",
            "schema": {
              "type": "string",
              "description": "Keep entities whose creators include this name (case-insensitive phrase match). Placeholders never match.",
              "minLength": 1,
              "maxLength": 120,
              "examples": [
                "Luke F. Walton"
              ]
            },
            "example": "Luke F. Walton"
          },
          {
            "name": "speaker",
            "in": "query",
            "required": false,
            "description": "Keep fragments whose speakers include this name. 'Other' and 'Unverified' are placeholders and never match.",
            "schema": {
              "type": "string",
              "description": "Keep fragments whose speakers include this name. 'Other' and 'Unverified' are placeholders and never match.",
              "minLength": 1,
              "maxLength": 120,
              "examples": [
                "Luke F. Walton"
              ]
            },
            "example": "Luke F. Walton"
          },
          {
            "name": "exposure",
            "in": "query",
            "required": false,
            "description": "Exposures to keep, comma-separated: text (public passage), semantic (released gist), locator (location only).",
            "schema": {
              "type": "string",
              "description": "Exposures to keep, comma-separated: text (public passage), semantic (released gist), locator (location only).",
              "pattern": "^(text|semantic|locator)(,(text|semantic|locator))*$",
              "examples": [
                "text,semantic"
              ]
            },
            "example": "text,semantic"
          },
          {
            "name": "raw",
            "in": "query",
            "required": false,
            "description": "Keep only one layer: public or private.",
            "schema": {
              "type": "string",
              "description": "Keep only one layer: public or private.",
              "enum": [
                "public",
                "private"
              ]
            }
          },
          {
            "name": "recency",
            "in": "query",
            "required": false,
            "description": "Recency weighting. Default 'none' here; 'prefer-recent' always favors recent material; 'auto' does so when the question asks about the present.",
            "schema": {
              "type": "string",
              "description": "Recency weighting. Default 'none' here; 'prefer-recent' always favors recent material; 'auto' does so when the question asks about the present.",
              "enum": [
                "none",
                "prefer-recent",
                "auto"
              ],
              "default": "none"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked hits with the fixed policy copy.",
            "headers": {
              "Cache-Control": {
                "description": "public, s-maxage=86400, stale-while-revalidate=604800",
                "schema": {
                  "type": "string"
                }
              },
              "X-Archive-Contract": {
                "description": "archive-search/1",
                "schema": {
                  "type": "string"
                }
              },
              "X-Cache": {
                "description": "hit or miss",
                "schema": {
                  "type": "string",
                  "enum": [
                    "hit",
                    "miss"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "`invalid-request`, with every problem named in `details`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate-limited`; 30 requests per minute per caller.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`index-not-published` until the archive republishes its index; `engine-unavailable` otherwise.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/archive/health": {
      "get": {
        "operationId": "archiveHealth",
        "summary": "Warm the index and report the served counts.",
        "responses": {
          "200": {
            "description": "The served index is loaded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "ok"
                    },
                    "builtAt": {
                      "type": "string"
                    },
                    "entityCount": {
                      "type": "integer"
                    },
                    "fragmentCount": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "status",
                    "builtAt",
                    "entityCount",
                    "fragmentCount"
                  ]
                }
              }
            }
          },
          "503": {
            "description": "The index is not published (`index-not-published`) or unreachable (`index-unavailable`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "unavailable"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "status",
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/archive/mcp": {
      "post": {
        "operationId": "mcp",
        "summary": "Model Context Protocol endpoint: Streamable HTTP, stateless, JSON responses, one tool (`search_archive`).",
        "description": "Send one JSON-RPC 2.0 message per request. Protocol revision 2026-07-28 (per-request `_meta`) and the 2025 `initialize` handshake are both accepted; no session is kept. The tool runs the same search as GET /api/archive/search under the same limiter and cache. GET on this path answers 405 (no server-sent event stream is offered).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "A JSON-RPC 2.0 request or notification."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON-RPC 2.0 response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "A notification or a client response was accepted; no body."
          },
          "400": {
            "description": "A JSON-RPC 2.0 error: parse error, invalid request, or an `Mcp-Method` header that does not match the body.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed: this endpoint accepts POST (and OPTIONS).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "SearchResponse": {
        "type": "object",
        "properties": {
          "contract": {
            "type": "string",
            "const": "archive-search/1"
          },
          "query": {
            "type": "string"
          },
          "hits": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvidenceHit"
            }
          },
          "matched": {
            "type": "integer",
            "description": "Hits above the score floor after filters, before `limit`."
          },
          "excludedUndated": {
            "type": "integer",
            "description": "Fragments dropped by a date bound because they carry no date."
          },
          "index": {
            "type": "object",
            "description": "Served counts. The embedding model and dimensions are never reported.",
            "properties": {
              "builtAt": {
                "type": "string"
              },
              "entityCount": {
                "type": "integer"
              },
              "fragmentCount": {
                "type": "integer"
              }
            },
            "required": [
              "builtAt",
              "entityCount",
              "fragmentCount"
            ]
          },
          "policy": {
            "type": "object",
            "description": "Fixed copy stating what each exposure means. Identical in every response.",
            "properties": {
              "exposure": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string"
                  },
                  "semantic": {
                    "type": "string"
                  },
                  "locator": {
                    "type": "string"
                  }
                },
                "required": [
                  "text",
                  "semantic",
                  "locator"
                ]
              },
              "note": {
                "type": "string"
              }
            },
            "required": [
              "exposure",
              "note"
            ]
          }
        },
        "required": [
          "contract",
          "query",
          "hits",
          "matched",
          "excludedUndated",
          "index",
          "policy"
        ]
      },
      "EvidenceHit": {
        "description": "A union on `exposure`: the wire type and the disclosure boundary. A private fragment's text has no field to travel in.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/TextHit"
          },
          {
            "$ref": "#/components/schemas/SemanticHit"
          },
          {
            "$ref": "#/components/schemas/LocatorHit"
          }
        ],
        "discriminator": {
          "propertyName": "exposure"
        }
      },
      "TextHit": {
        "type": "object",
        "description": "The passage is public and is included verbatim. Quote it with its entity.url.",
        "properties": {
          "fragmentId": {
            "type": "string"
          },
          "entity": {
            "type": "object",
            "description": "The work the fragment belongs to. Cite `url`.",
            "properties": {
              "id": {
                "type": "string"
              },
              "type": {
                "type": "string",
                "description": "song, album, writing, letter, episode, transcript, publication, interview, page, ..."
              },
              "title": {
                "type": "string"
              },
              "attribution": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Attribution"
                },
                "description": "Creators."
              },
              "date": {
                "type": "string",
                "description": "YYYY, YYYY-MM, or YYYY-MM-DD. Absent means unknown, never guessed."
              },
              "version": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "The accountable public page. The citation target."
              },
              "identifiers": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Identifier"
                }
              },
              "parent": {
                "type": "string",
                "description": "The entity this one belongs to (a transcript's episode page)."
              },
              "themes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "id",
              "type",
              "title",
              "attribution",
              "url",
              "identifiers"
            ]
          },
          "raw": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "description": "The policy layer, not a content field."
          },
          "locator": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Locator"
            }
          },
          "locatorLabel": {
            "type": "string",
            "description": "Rendered from `locator`: \"p. 184\", \"12:30–14:05\", \"whole record\"."
          },
          "date": {
            "type": "string",
            "description": "The effective date: the fragment's, else the entity's."
          },
          "attribution": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attribution"
            },
            "description": "Speakers in this window, when known."
          },
          "score": {
            "type": "number",
            "description": "Public hits: full precision. Private hits: rounded to 0.05."
          },
          "breakdown": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Public hits only."
          },
          "exposure": {
            "type": "string",
            "const": "text"
          },
          "text": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          }
        },
        "required": [
          "fragmentId",
          "entity",
          "raw",
          "locator",
          "locatorLabel",
          "score",
          "exposure",
          "text"
        ]
      },
      "SemanticHit": {
        "type": "object",
        "description": "The source is not quotable here. gist is a description of what the passage is about, drafted by the archive's software at ingest and released under the archive owner's policy; gistSource says whether a person edited it and gistReview whether a person reviewed it. It is not a quotation and not the creators' wording.",
        "properties": {
          "fragmentId": {
            "type": "string"
          },
          "entity": {
            "type": "object",
            "description": "The work the fragment belongs to. Cite `url`.",
            "properties": {
              "id": {
                "type": "string"
              },
              "type": {
                "type": "string",
                "description": "song, album, writing, letter, episode, transcript, publication, interview, page, ..."
              },
              "title": {
                "type": "string"
              },
              "attribution": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Attribution"
                },
                "description": "Creators."
              },
              "date": {
                "type": "string",
                "description": "YYYY, YYYY-MM, or YYYY-MM-DD. Absent means unknown, never guessed."
              },
              "version": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "The accountable public page. The citation target."
              },
              "identifiers": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Identifier"
                }
              },
              "parent": {
                "type": "string",
                "description": "The entity this one belongs to (a transcript's episode page)."
              },
              "themes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "id",
              "type",
              "title",
              "attribution",
              "url",
              "identifiers"
            ]
          },
          "raw": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "description": "The policy layer, not a content field."
          },
          "locator": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Locator"
            }
          },
          "locatorLabel": {
            "type": "string",
            "description": "Rendered from `locator`: \"p. 184\", \"12:30–14:05\", \"whole record\"."
          },
          "date": {
            "type": "string",
            "description": "The effective date: the fragment's, else the entity's."
          },
          "attribution": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attribution"
            },
            "description": "Speakers in this window, when known."
          },
          "score": {
            "type": "number",
            "description": "Public hits: full precision. Private hits: rounded to 0.05."
          },
          "breakdown": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Public hits only."
          },
          "exposure": {
            "type": "string",
            "const": "semantic"
          },
          "gist": {
            "type": "string",
            "description": "An authorized description of what the passage is about. Not a quotation."
          },
          "gistSource": {
            "type": "string",
            "enum": [
              "generated",
              "edited"
            ]
          },
          "gistReview": {
            "type": "string",
            "enum": [
              "unreviewed",
              "reviewed"
            ]
          }
        },
        "required": [
          "fragmentId",
          "entity",
          "raw",
          "locator",
          "locatorLabel",
          "score",
          "exposure",
          "gist",
          "gistSource",
          "gistReview"
        ]
      },
      "LocatorHit": {
        "type": "object",
        "description": "Only the location of relevant material is released. Do not infer its contents.",
        "properties": {
          "fragmentId": {
            "type": "string"
          },
          "entity": {
            "type": "object",
            "description": "The work the fragment belongs to. Cite `url`.",
            "properties": {
              "id": {
                "type": "string"
              },
              "type": {
                "type": "string",
                "description": "song, album, writing, letter, episode, transcript, publication, interview, page, ..."
              },
              "title": {
                "type": "string"
              },
              "attribution": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Attribution"
                },
                "description": "Creators."
              },
              "date": {
                "type": "string",
                "description": "YYYY, YYYY-MM, or YYYY-MM-DD. Absent means unknown, never guessed."
              },
              "version": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "The accountable public page. The citation target."
              },
              "identifiers": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Identifier"
                }
              },
              "parent": {
                "type": "string",
                "description": "The entity this one belongs to (a transcript's episode page)."
              },
              "themes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "id",
              "type",
              "title",
              "attribution",
              "url",
              "identifiers"
            ]
          },
          "raw": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "description": "The policy layer, not a content field."
          },
          "locator": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Locator"
            }
          },
          "locatorLabel": {
            "type": "string",
            "description": "Rendered from `locator`: \"p. 184\", \"12:30–14:05\", \"whole record\"."
          },
          "date": {
            "type": "string",
            "description": "The effective date: the fragment's, else the entity's."
          },
          "attribution": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attribution"
            },
            "description": "Speakers in this window, when known."
          },
          "score": {
            "type": "number",
            "description": "Public hits: full precision. Private hits: rounded to 0.05."
          },
          "breakdown": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Public hits only."
          },
          "exposure": {
            "type": "string",
            "const": "locator"
          }
        },
        "required": [
          "fragmentId",
          "entity",
          "raw",
          "locator",
          "locatorLabel",
          "score",
          "exposure"
        ]
      },
      "Attribution": {
        "type": "object",
        "description": "Who made or spoke something. An entry with `placeholder` is not a person: `unnamed` is a speaker who is not a creator, `unverified` is attribution not established.",
        "properties": {
          "name": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "description": "performer, writer, author, host, guest, speaker, composer, remixer, or a stated production role."
          },
          "identifiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Identifier"
            }
          },
          "placeholder": {
            "type": "string",
            "enum": [
              "unnamed",
              "unverified"
            ]
          }
        },
        "required": [
          "name"
        ]
      },
      "Identifier": {
        "type": "object",
        "description": "A catalogue number on an entity or a person; the scheme set is open (isrc, doi, isbn, orcid, isni, ipi, spotify, apple, url, ...).",
        "properties": {
          "scheme": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        },
        "required": [
          "scheme",
          "value"
        ]
      },
      "Locator": {
        "type": "object",
        "description": "Where a fragment sits in its entity; `end` only for ranges. `timecode` values are decimal seconds.",
        "properties": {
          "scheme": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "end": {
            "type": "string"
          }
        },
        "required": [
          "scheme",
          "value"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "details": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "error"
        ]
      }
    }
  }
}