{
  "openapi": "3.1.0",
  "info": {
    "title": "Trooth Public API",
    "version": "1.1.0",
    "summary": "The read-only public surface of the Trooth Network.",
    "description": "Everything here answers a question about a software or AI company from published\nevidence, and needs no credential. It is the surface a person, an integrator or an\nauthorized agent may build on.\n\nWHAT IS DELIBERATELY ABSENT. Authentication flows are not documented: they are\nbrowser redirects in a login sequence, not an API. Unauthenticated endpoints that\nsend mail or start work are rate limited and undocumented on purpose. Every\nsession-authenticated endpoint is absent because publishing an internal route map\nhelps an attacker and promises an API that is not offered.\n\nEVIDENCE VOCABULARY. Responses distinguish witnessed, confirmed, declared, inferred,\nunknown, unavailable, stale and conflicting. Unknown is an answer, never a blank\nfilled in, and stale is never the same as wrong. No response carries a figure, a\nrank or a grade for a company: the witness facts are coverage (checks passed of\nchecks run), freshness (last read), continuity (unbroken since) and change.\n\nA 404 from these endpoints usually means the network holds no record, which is an\nanswer rather than a failure.\n\nGenerated from app/api by scripts/gen-openapi.mjs. check:openapi fails the build\nwhen this document and the source disagree.",
    "contact": {
      "name": "Trooth developer support",
      "email": "developers@trooth.co",
      "url": "https://trooth.co/developers"
    },
    "license": {
      "name": "Terms of Service",
      "url": "https://trooth.co/terms"
    }
  },
  "servers": [
    {
      "url": "https://trooth.co",
      "description": "Production"
    }
  ],
  "x-counts": {
    "paths": 9,
    "operations": 9
  },
  "paths": {
    "/api/mobile/directory/search": {
      "get": {
        "summary": "Search the Trooth Network",
        "description": "Full-text search over published company listings. Returns the same companies the public directory shows. Prefer cursor pagination: pass the returned next_cursor back as cursor. Offset pagination still works and is kept for clients that already ship with it, but it counts rows rather than naming a position, so a directory that grows between two requests can serve the same company twice and skip another without erroring. A cursor that cannot be read returns 400 rather than an empty page. Cached at the edge for 60 seconds with a 300 second stale-while-revalidate window.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search terms. Matched against company name and description.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "industry",
            "in": "query",
            "required": false,
            "description": "Restrict to one industry.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Results per page. Clamped to 100; anything unreadable falls back to 25.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque page marker. Use next_cursor from the previous response. Preferred over offset.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Zero-based offset. Use next_offset from the previous response. Superseded by cursor.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "operationId": "get_api_mobile_directory_search",
        "security": [],
        "responses": {
          "200": {
            "description": "Success"
          },
          "404": {
            "description": "No such record. Not an error; the network does not hold it."
          }
        }
      }
    },
    "/api/mobile/directory/featured": {
      "get": {
        "summary": "Companies with a witnessed standing",
        "description": "The network's front page: published companies, those with a witnessed standing first. Cached at the edge for 120 seconds.",
        "operationId": "get_api_mobile_directory_featured",
        "security": [],
        "responses": {
          "200": {
            "description": "Success"
          },
          "404": {
            "description": "No such record. Not an error; the network does not hold it."
          }
        }
      }
    },
    "/api/mobile/directory/vendor/{slug}": {
      "get": {
        "summary": "One company record",
        "description": "A published company profile, and its live standing where the company has been witnessed. A slug of the form d--example.com resolves a witnessed company that has not yet published a rich profile. Cached at the edge for 120 seconds.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Company slug, or d--<domain>.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "operationId": "get_api_mobile_directory_vendor_slug_",
        "security": [],
        "responses": {
          "200": {
            "description": "Success"
          },
          "404": {
            "description": "No such record. Not an error; the network does not hold it."
          }
        }
      }
    },
    "/api/public-mesh/{handle}": {
      "get": {
        "summary": "A company's public evidence rollup",
        "description": "The buyer-facing projection of a company's evidence: the witnessed state per pillar, framework rollups and provider status. Individual control rows, owners and internal notes are never returned here. Responds 404 when the company has no public record, which is a privacy-preserving answer rather than an error. Readable cross-origin (Access-Control-Allow-Origin: *); this is the read the embeddable badge performs. Cached at the edge for 300 seconds.",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Company handle.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "operationId": "get_api_public_mesh_handle_",
        "security": [],
        "responses": {
          "200": {
            "description": "Success"
          },
          "404": {
            "description": "No such record. Not an error; the network does not hold it."
          }
        }
      }
    },
    "/api/network/profile": {
      "get": {
        "summary": "One company's canonical profile and witness facts",
        "description": "The canonical published profile for a company, keyed by domain or slug, with the witness facts a buyer's tool needs: standing, lastWitnessed (freshness), firstWitnessedAt and the unbroken run of Trooth's own hourly readings (continuity), and checksPassed of checksRun on the signed result (coverage). Continuity is never claimed older than Trooth's own record and stays null until two consecutive readings agree. Responds { found: false } with 200 when no profile is published, 400 when q is missing. Cached at the edge for 120 seconds. Every response carries a `methodology` object stating the reading cadence, the gap tolerance that defines an unbroken series, the retention period, the per-category freshness windows, and an explicit list of coverage limits - what Trooth does not read. A caller restating `witnessed` or `updatedAt` elsewhere must carry those limits with it: neither field is a certification nor an audit opinion.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Company domain (example.com) or Trooth slug.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "contract",
            "in": "query",
            "required": false,
            "description": "Pin the response contract version. Omit for whatever this deployment serves. A version this deployment does not answer is refused with 400 rather than approximated, because a caller that pins is pinning precisely to avoid guessing. Every response carries contractVersion, a note saying when that integer moves, and an explicit list of what the contract does NOT carry.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "operationId": "get_api_network_profile",
        "security": [],
        "responses": {
          "200": {
            "description": "Success"
          },
          "400": {
            "description": "The q parameter is missing, or a contract version this deployment does not answer was requested."
          },
          "404": {
            "description": "No such record. Not an error; the network does not hold it."
          }
        }
      }
    },
    "/api/compare": {
      "get": {
        "summary": "Compare companies side by side",
        "description": "Verdict-only comparison across up to a handful of companies. Control rows are stripped, so a company's internal posture is never exposed through the comparison. Rate limited.",
        "parameters": [
          {
            "name": "handles",
            "in": "query",
            "required": true,
            "description": "Comma-separated company handles.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "operationId": "get_api_compare",
        "security": [],
        "responses": {
          "200": {
            "description": "Success"
          },
          "404": {
            "description": "No such record. Not an error; the network does not hold it."
          },
          "429": {
            "description": "Rate limited. Retry after a short pause."
          }
        }
      }
    },
    "/api/network/suggest": {
      "get": {
        "summary": "Typeahead over published companies",
        "description": "Name-to-company suggestions for a search box, over the same published listings the directory shows. Cached at the edge for 60 seconds.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Partial company name.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "operationId": "get_api_network_suggest",
        "security": [],
        "responses": {
          "200": {
            "description": "Success"
          },
          "404": {
            "description": "No such record. Not an error; the network does not hold it."
          }
        }
      }
    },
    "/api/legal/{slug}": {
      "get": {
        "summary": "A published legal document",
        "description": "The full text of one of the twenty published Trooth legal documents, with its title, effective date and body. The slug must be one of the published set; anything else responds 404. Cached at the edge for one hour with a one day stale-while-revalidate window.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Document slug, for example privacy, terms, subprocessors.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "operationId": "get_api_legal_slug_",
        "security": [],
        "responses": {
          "200": {
            "description": "Success"
          },
          "404": {
            "description": "No such record. Not an error; the network does not hold it."
          }
        }
      }
    },
    "/api/version": {
      "get": {
        "summary": "The deployment currently serving",
        "description": "The build stamp of the running deployment. Never cached. Useful for confirming which release answered a request.",
        "operationId": "get_api_version",
        "security": [],
        "responses": {
          "200": {
            "description": "Success"
          },
          "404": {
            "description": "No such record. Not an error; the network does not hold it."
          }
        }
      }
    }
  }
}
