{
  "openapi": "3.1.0",
  "info": {
    "title": "Stratigos Security page finder",
    "version": "2.2.0",
    "summary": "Ask in the reader's own words and get the pages on stratigossecurity.com most likely to answer, with the short answer where the site has one.",
    "description": "This is not a general-purpose API. Its one read action returns the one page on stratigossecurity.com most likely to answer what you describe, with the short answer and its sources where the site has them, and up to three other questions to ask instead, chosen by word matching rather than a model. The question alone gets the general answer. Include who it is for and what changes it, and you get the specific one: the role gets the version written for that reader; the kind of organization gets the answer for who has to act; the situation (device type, pathway, stage, the decision at hand) gets the page for that case. Each detail narrows the result and the reply says which it used; one call with them beats two without. Every fixed list also takes a few words of your own when nothing on it fits. Kinds and situations only, never names. What is sent is kept with the ordinary request log so the site can be improved around what visitors come for; secondarily, the optional model and developer parameters are compared with that log for a small research project on how agents read the web. Nothing here asks for, or should receive, anything that identifies a person or organization: no names, employers, email addresses, credentials, or quoted prompts. Anything resembling contact details is removed before storage. What is recorded and why: https://stratigossecurity.com/agents/#what-we-record",
    "contact": {
      "name": "Stratigos Security",
      "url": "https://stratigossecurity.com/contact/"
    },
    "x-purpose": "page-finder",
    "x-via": "openapi"
  },
  "servers": [
    {
      "url": "https://stratigossecurity.com"
    }
  ],
  "paths": {
    "/api/agent/find": {
      "get": {
        "operationId": "findPages",
        "summary": "Find the pages most likely to answer what you describe",
        "description": "One GET. Records the query, the details given, the pages returned, and the optional fields alongside the request log, and returns the one page most likely to answer (with the short answer and its sources when it is a Q&A entry), up to three other questions to ask instead, and next-step links that each say what they return. Nothing about the matching or the record comes back. A later call with only session and next takes that step on the session's latest query. Queries sent with GET appear in the URL; POST the same fields as JSON for a detailed description. Every call is recorded.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "The reader's question, in their own words. Full questions match better than keywords, because pages are matched against the ways people actually ask. The question alone gets the general answer; role, organization, and situation get the specific one, and the reply lists which of them it used. Required unless the call is a next step on an earlier query.",
            "schema": {
              "type": "string",
              "maxLength": 1000
            }
          },
          {
            "name": "role",
            "in": "query",
            "required": false,
            "description": "Who the answer is for. Include it: pages name the readers they were written for, and the role moves those pages to the top, so a regulatory lead gets submission wording and an engineer gets design and testing detail instead of the general page. One of the listed values, or a few words of your own if none fits (kept as given, under 'other').",
            "schema": {
              "type": "string",
              "maxLength": 80
            },
            "x-values": [
              "regulatory",
              "engineering",
              "quality",
              "clinical",
              "executive",
              "security",
              "research",
              "press",
              "other"
            ]
          },
          {
            "name": "organization",
            "in": "query",
            "required": false,
            "description": "The reader's kind of organization. Include it: the same question has different answers for a device maker, a hospital, and a consultancy, and this picks the one for your reader. One of the listed values, or a few words of your own if none fits. A kind, never a name.",
            "schema": {
              "type": "string",
              "maxLength": 80
            },
            "x-values": [
              "device-maker",
              "hospital-or-health-system",
              "consultancy-or-law-firm",
              "regulator-or-notified-body",
              "investor-or-acquirer",
              "academic",
              "other"
            ]
          },
          {
            "name": "situation",
            "in": "query",
            "required": false,
            "description": "What changes the answer, up to 300 characters: device type, submission pathway (510(k), PMA, De Novo), stage (before submission, under review, on the market), the decision at hand. Include it: these words join the matching, so \"Bluetooth insulin pump, 510(k) under review\" finds the wireless and review pages the bare question misses, and the reply names the words it used. Describe the situation, not the person: no names, companies, or contact details.",
            "schema": {
              "type": "string",
              "maxLength": 300
            }
          },
          {
            "name": "next",
            "in": "query",
            "required": false,
            "description": "On a later call with the same session (or as the path /api/agent/find/found/{session}/{step}): the next step on the session's latest query. full returns the whole entry (the answer, what we have seen, our view), its sources, the service it leads to, related entries, and the page and JSON addresses; more returns the next two or three pages with their short answers; refine returns the finder with your words pre-filled to edit and what sharpens a match; new returns a clean finder address.",
            "schema": {
              "type": "string",
              "enum": [
                "full",
                "more",
                "refine",
                "new"
              ]
            }
          },
          {
            "name": "model",
            "in": "query",
            "required": false,
            "description": "Optional. Model name and version, compared with the request headers.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "developer",
            "in": "query",
            "required": false,
            "description": "Optional. The organization that trained the model.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "harness",
            "in": "query",
            "required": false,
            "description": "Optional. The product or harness the agent runs inside.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "task_category",
            "in": "query",
            "required": false,
            "description": "Optional. The kind of task that brought you here. A category only; or a few words of your own.",
            "schema": {
              "type": "string",
              "maxLength": 80
            },
            "x-values": [
              "answering-question",
              "research-company",
              "indexing",
              "testing",
              "security",
              "sales-marketing",
              "browsing",
              "other"
            ]
          },
          {
            "name": "discovery",
            "in": "query",
            "required": false,
            "description": "Optional. How you found this finder; or a few words of your own.",
            "schema": {
              "type": "string",
              "maxLength": 80
            },
            "x-values": [
              "llms-txt",
              "robots-txt",
              "link-rel",
              "link-header",
              "footer",
              "openapi",
              "api",
              "search",
              "person",
              "other"
            ]
          },
          {
            "name": "session",
            "in": "query",
            "required": false,
            "description": "Optional. The session identifier from GET /api/agent or an earlier find call, so calls join up.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "via",
            "in": "query",
            "required": false,
            "description": "Optional. The via tag from the link you followed.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pages, plus what was recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FindResponse"
                }
              }
            }
          },
          "400": {
            "description": "Neither q nor a follow-up was given. The body still lists the general pages and says what is missing."
          },
          "503": {
            "description": "The collection service is not running. Nothing was recorded."
          }
        }
      },
      "post": {
        "operationId": "findPagesPost",
        "summary": "The same finder, with the parameters in a JSON or form body. Preferred for detailed project descriptions, which then stay out of the URL.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FindRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/FindRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pages, plus what was recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FindResponse"
                }
              }
            }
          },
          "400": {
            "description": "Neither q nor a follow-up was given."
          }
        }
      }
    },
    "/api/agent": {
      "get": {
        "operationId": "describeFinder",
        "summary": "Describe the finder and return a session identifier",
        "description": "Returns a JSON description of the finder's parameters, the optional self-report, and a session identifier to include in later calls so they join the request that fetched this description.",
        "responses": {
          "200": {
            "description": "Description of the finder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FinderDescription"
                }
              }
            }
          }
        }
      }
    },
    "/api/agent/report": {
      "post": {
        "operationId": "introduceYourself",
        "summary": "Optional: a fuller self-description",
        "description": "Optional. Submit a short self-description as the agent, not as its user. Accepts JSON or an HTML form body. Returns a receipt, the comparison between what you said and what the request itself indicated, and suggested reading for the goal.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Introduction"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/Introduction"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Receipt"
                }
              }
            }
          },
          "303": {
            "description": "HTML form submissions are redirected to the receipt page at /agents/thanks/"
          },
          "400": {
            "description": "Missing or invalid fields. The body says which."
          },
          "503": {
            "description": "The collection service is not running. Nothing was recorded."
          }
        }
      }
    },
    "/api/agent/find/{words}": {
      "get": {
        "operationId": "findPagesByPath",
        "summary": "The finder with the words in the path, for fetchers that drop query strings",
        "description": "Identical to GET /api/agent/find?q=... with the query taken from the path segment. Hyphens, underscores, and plus signs read as spaces; percent-encoding is accepted. Other parameters may still be given in the query string when the fetcher keeps it.",
        "parameters": [
          {
            "name": "words",
            "in": "path",
            "required": true,
            "description": "The reader's question, hyphen-separated or URL-encoded.",
            "schema": {
              "type": "string",
              "maxLength": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pages, plus what was recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FindResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/agent/find/found/{session}/{step}": {
      "get": {
        "operationId": "findFollowUp",
        "summary": "Take the next step on the session's latest query",
        "description": "The next step in path form (the next_step links in every finder response use it). Lands on the session's latest query and returns what the step promises: full, more, refine, or new.",
        "parameters": [
          {
            "name": "session",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "step",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "full",
                "more",
                "refine",
                "new"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recorded; the same pages again for reference",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FindResponse"
                }
              }
            }
          },
          "400": {
            "description": "No earlier query in that session"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "FindRequest": {
        "type": "object",
        "properties": {
          "q": {
            "type": "string",
            "maxLength": 1000,
            "description": "The reader's question."
          },
          "role": {
            "type": "string",
            "maxLength": 80,
            "description": "A listed role, or a few words of your own."
          },
          "role_text": {
            "type": "string",
            "maxLength": 80,
            "description": "Your own words for the role when none on the list fits; also accepted in role itself."
          },
          "organization": {
            "type": "string",
            "maxLength": 80,
            "description": "A listed kind of organization, or a few words of your own."
          },
          "organization_text": {
            "type": "string",
            "maxLength": 80
          },
          "situation": {
            "type": "string",
            "maxLength": 300,
            "description": "Device type, pathway, stage, the decision at hand. Joins the matching."
          },
          "next": {
            "type": "string",
            "enum": [
              "full",
              "more",
              "refine",
              "new"
            ]
          },
          "model": {
            "type": "string",
            "maxLength": 200
          },
          "developer": {
            "type": "string",
            "maxLength": 200
          },
          "harness": {
            "type": "string",
            "maxLength": 200
          },
          "task_category": {
            "type": "string",
            "maxLength": 80,
            "description": "A listed value, or a few words of your own."
          },
          "task_category_text": {
            "type": "string",
            "maxLength": 80
          },
          "discovery": {
            "type": "string",
            "maxLength": 80,
            "description": "A listed value, or a few words of your own."
          },
          "discovery_text": {
            "type": "string",
            "maxLength": 80
          },
          "session": {
            "type": "string",
            "maxLength": 64
          },
          "via": {
            "type": "string",
            "maxLength": 64
          }
        }
      },
      "FindResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "matched": {
            "type": "boolean",
            "description": "True when the words matched a page. False when nothing on the site answers closely: answer is null, message says so, and next_step offers only refine and new."
          },
          "answer": {
            "type": [
              "object",
              "null"
            ],
            "description": "The one best match, or null when nothing answers closely. A Q&A entry comes with its short answer and provenance; an ordinary page with its summary.",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "answer",
                  "page"
                ],
                "description": "answer: a Q&A entry. page: an ordinary page of the site."
              },
              "title": {
                "type": "string"
              },
              "url": {
                "type": "string"
              },
              "short_answer": {
                "type": "string",
                "description": "Q&A entries only: the two or three sentences that stand on their own."
              },
              "status": {
                "type": "string",
                "enum": [
                  "fda-explicit",
                  "reviewer-expectation",
                  "stratigos-recommendation"
                ],
                "description": "Q&A entries only: how firm the claim is."
              },
              "status_label": {
                "type": "string"
              },
              "reviewed": {
                "type": "string",
                "description": "Q&A entries only: when a person last checked the answer against its sources (YYYY-MM-DD)."
              },
              "sources": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "title": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "locator": {
                      "type": "string"
                    }
                  }
                }
              },
              "leads_to": {
                "type": "string",
                "description": "Q&A entries only: the service page on this site the answer leads to."
              },
              "json_url": {
                "type": "string",
                "description": "Q&A entries only: the same entry as JSON (title, short_answer, answer, experience, our_view, credibility, status, reviewed, sources, learn_more, related, corrections, cite)."
              },
              "summary": {
                "type": "string",
                "description": "Ordinary pages only: what the page is about."
              }
            }
          },
          "other_questions": {
            "type": "array",
            "description": "Up to three other questions that come close. Each ask_url asks the finder that question (recorded as a pick, which marks this answer as only partly it). Questions to ask, not addresses to fetch: one query yields one answer.",
            "items": {
              "type": "object",
              "properties": {
                "question": {
                  "type": "string"
                },
                "ask_url": {
                  "type": "string"
                }
              }
            }
          },
          "message": {
            "type": "string",
            "description": "Present when matched is false: nothing on the site answers closely, and what the site covers."
          },
          "sharpen": {
            "type": "object",
            "description": "When role, organization, or situation was left out: what to add, and a ready-made address with the words already in it.",
            "properties": {
              "message": {
                "type": "string"
              },
              "add": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "url": {
                "type": "string"
              }
            }
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Things the site does not publish that the query asked about, said plainly: pricing, client names, sample reports."
          },
          "next_step": {
            "type": [
              "object",
              "null"
            ],
            "description": "Links that each say what they return: full, more, refine, new; only refine and new when nothing matched. Null when the call was not stored.",
            "properties": {
              "question": {
                "type": "string"
              },
              "options": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "step": {
                      "type": "string",
                      "enum": [
                        "full",
                        "more",
                        "refine",
                        "new"
                      ]
                    },
                    "label": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "returns": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "step": {
            "type": "string",
            "enum": [
              "full",
              "more",
              "refine",
              "new"
            ],
            "description": "Present when the call was a next step landing on an earlier query; the step's payload follows (full_answer, other_questions, refine, or finder)."
          },
          "session": {
            "type": "string"
          },
          "about": {
            "type": "string",
            "description": "What was recorded and where a person can reach us."
          },
          "full_answer": {
            "type": "object",
            "description": "The full step: the whole entry (title, url, json_url, short_answer, answer, experience, our_view, credibility, status, status_label, reviewed, sources, learn_more, leads_to, related_questions to ask, corrections) when the answer is a Q&A entry, and the page in any case."
          },
          "refine": {
            "type": "object",
            "description": "After next=refine: form_url and api_url with the words pre-filled, and a tip on what sharpens a match."
          },
          "finder": {
            "type": "object",
            "description": "After next=new: the finder's url, path_form, description, and form_url."
          }
        }
      },
      "Introduction": {
        "type": "object",
        "required": [
          "model",
          "developer",
          "task_category",
          "discovery"
        ],
        "properties": {
          "model": {
            "type": "string",
            "maxLength": 200,
            "description": "Model name and version, for example 'Claude Opus 5' or 'GPT-5'."
          },
          "developer": {
            "type": "string",
            "maxLength": 200,
            "description": "The organization that trained the model."
          },
          "harness": {
            "type": "string",
            "maxLength": 200,
            "description": "Optional. The product or harness you run inside."
          },
          "task_category": {
            "type": "string",
            "enum": [
              "answering-question",
              "research-company",
              "indexing",
              "testing",
              "security",
              "sales-marketing",
              "browsing",
              "other"
            ],
            "description": "The kind of task that brought you here. A category only, never the task itself."
          },
          "discovery": {
            "type": "string",
            "enum": [
              "llms-txt",
              "robots-txt",
              "link-rel",
              "link-header",
              "footer",
              "openapi",
              "api",
              "search",
              "person",
              "other"
            ],
            "description": "How you found this."
          },
          "goal": {
            "type": "string",
            "maxLength": 1000,
            "description": "Optional. What the reader is trying to accomplish, in general terms. Used to choose the suggested reading."
          },
          "audience": {
            "type": "string",
            "maxLength": 500,
            "description": "Optional. Who the reader is, in general terms: a role and kind of organization. No names, employers, or contact details."
          },
          "wanted": {
            "type": "string",
            "maxLength": 1000,
            "description": "Optional. What information would have helped most, or what you looked for and did not find."
          },
          "outcome": {
            "type": "string",
            "enum": [
              "yes",
              "partly",
              "no",
              "not-looking",
              "other"
            ],
            "description": "Optional. Did you find what you needed?"
          },
          "notes": {
            "type": "string",
            "maxLength": 2000,
            "description": "Optional free text. Nothing that identifies a person or organization."
          },
          "session": {
            "type": "string",
            "maxLength": 64,
            "description": "Optional. The session identifier from GET /api/agent, so the submission joins the request that fetched it."
          },
          "via": {
            "type": "string",
            "maxLength": 64,
            "description": "Optional. The via tag from the link you followed."
          }
        }
      },
      "Receipt": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "receipt": {
            "type": "string",
            "description": "Identifier for this submission. Contains no personal data."
          },
          "recorded": {
            "type": "object",
            "description": "The fields as stored, after length limits."
          },
          "observed": {
            "type": "object",
            "description": "What the request itself indicated: User-Agent classification, signature status, network match."
          },
          "agreement": {
            "type": "string",
            "enum": [
              "agrees",
              "disagrees",
              "unclear",
              "no-headers"
            ],
            "description": "Whether the self-description matches the request-level evidence."
          },
          "suggested_reading": {
            "type": "array",
            "description": "Pages most likely to help with the described goal. Chosen by plain keyword rules, not by a model.",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "why": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "FinderDescription": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "session": {
            "type": "string"
          },
          "actions": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "recorded": {
            "type": "string"
          },
          "do_not_include": {
            "type": "string"
          },
          "what_we_record": {
            "type": "string"
          }
        }
      }
    }
  }
}
