{
  "openapi": "3.1.0",
  "info": {
    "title": "AVOID.NET API",
    "version": "v1",
    "summary": "Crypto risk intelligence: investigations, trust scores and sourced evidence.",
    "description": "AVOID.NET is a crypto risk-intelligence index of exchanges, tokens, protocols and individuals, each with a 0-100 trust score (0 = high risk, 100 = trustworthy), sourced evidence and an on-chain-anchored audit history.\n\nThis document covers the public read API under /api/v1 plus /api/search. All reads are open: no endpoint currently requires or validates a bearer token. POST /api/v1/keys registers a keypair and returns a token (format `avoid_...`) so a client can establish an identity ahead of enforcement; today it grants no access.\n\nAutomated agents: please send a descriptive User-Agent, e.g. `my-agent/1.0 (+https://example.com/contact)`.\n\nOther machine-readable entry points: /llms.txt (what AVOID.NET is, how to cite it, the API and MCP summary) and /llms-full.txt (plain-text index of every investigation). The GET /api/v1 discovery document lists additional endpoints that are authenticated or internal and are intentionally not described here."
  },
  "servers": [
    {
      "url": "https://avoid.net"
    }
  ],
  "tags": [
    {
      "name": "Investigations",
      "description": "Public investigation pages."
    },
    {
      "name": "Registration",
      "description": "Keypair registration for machine access."
    },
    {
      "name": "MCP",
      "description": "Model Context Protocol server."
    },
    {
      "name": "A2A",
      "description": "Agent2Agent (A2A) protocol endpoint."
    },
    {
      "name": "Discovery",
      "description": "Self-description of the API."
    }
  ],
  "paths": {
    "/api/v1": {
      "get": {
        "operationId": "getApiIndex",
        "tags": [
          "Discovery"
        ],
        "summary": "API discovery document",
        "description": "Machine-readable index of the v1 API: endpoints, params, auth notes, MCP endpoint. It also lists authenticated/internal endpoints not described in this OpenAPI document.",
        "responses": {
          "200": {
            "description": "Discovery document. Shape is informal and may grow.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "docs": {
                      "type": "string",
                      "format": "uri"
                    },
                    "endpoints": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "auth": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "mcp": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/investigations": {
      "get": {
        "operationId": "listInvestigations",
        "tags": [
          "Investigations"
        ],
        "summary": "List investigations",
        "description": "Paginated list of public investigations. Unpublished pages (drafts, held, quarantined) are never listed, so `status=draft` returns an empty page. Out-of-range or non-numeric `limit`/`offset` are clamped or defaulted, not rejected. Edge-cached for 5 minutes.",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "description": "Filter by entity name (case-insensitive substring match).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter by status. Any other value matches nothing and returns an empty page.",
            "schema": {
              "type": "string",
              "enum": [
                "published",
                "under_investigation",
                "archived",
                "merged"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Sort field. Any value other than `updated` sorts by entity_name.",
            "schema": {
              "type": "string",
              "enum": [
                "entity_name",
                "updated"
              ],
              "default": "entity_name"
            }
          },
          {
            "name": "order",
            "in": "query",
            "description": "Sort direction. Any value other than `desc` sorts ascending.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Results per page (1-100).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of results to skip.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of investigation summaries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "investigations",
                    "total",
                    "limit",
                    "offset"
                  ],
                  "properties": {
                    "investigations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/InvestigationSummary"
                      }
                    },
                    "total": {
                      "type": "integer",
                      "description": "Total rows matching the filters, ignoring limit/offset."
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Database error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/investigations/{slug}": {
      "get": {
        "operationId": "getInvestigation",
        "tags": [
          "Investigations"
        ],
        "summary": "Get one investigation",
        "description": "Full structured investigation: sections, timeline, sources, trust score components, plus a source-quality tally and follower count. Edge-cached for 5 minutes.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Investigation slug, e.g. as returned by the list or lookup endpoints.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The investigation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "investigation",
                    "source_quality",
                    "follower_count"
                  ],
                  "properties": {
                    "investigation": {
                      "$ref": "#/components/schemas/InvestigationDetail"
                    },
                    "source_quality": {
                      "type": "object",
                      "description": "Distinct cited URLs counted by source tier. Informational; the trust score does not use it.",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "tier1": {
                          "type": "integer"
                        },
                        "tier2": {
                          "type": "integer"
                        },
                        "tier3": {
                          "type": "integer"
                        },
                        "unrated": {
                          "type": "integer"
                        }
                      }
                    },
                    "follower_count": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Number of accounts following the page. Null when fewer than 3 follow it, or if the count could not be read."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No public investigation with this slug.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/lookup": {
      "get": {
        "operationId": "lookup",
        "tags": [
          "Investigations"
        ],
        "summary": "Resolve an entity name or on-chain address",
        "description": "Finds public investigations by exact on-chain address and/or entity-name substring. At least one of `address` or `entity` is required; when both are given, address matches come first and duplicates are dropped. Each of the two searches returns at most 25 rows. Edge-cached for 5 minutes.",
        "parameters": [
          {
            "name": "address",
            "in": "query",
            "description": "On-chain address to look up.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "entity",
            "in": "query",
            "description": "Entity name (case-insensitive substring match).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching investigations (possibly none).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "matches",
                    "total"
                  ],
                  "properties": {
                    "matches": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LookupMatch"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Neither `address` nor `entity` was provided.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/investigations/{slug}/proof": {
      "get": {
        "operationId": "getDecisionProof",
        "tags": [
          "Investigations"
        ],
        "summary": "Merkle membership proof for one decision event",
        "description": "Compact Merkle proof that one decision event is included in the page's audit history, verifiable locally against the returned root. CAVEAT: the Merkle-root worker is not yet deployed, so no root has been anchored on Solana. Proofs are internally valid but server-attested; check that `root_matches_published` is true and `root_anchor_signature` is non-null before treating one as independent evidence. Edge-cached for 60 seconds.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Investigation slug, e.g. as returned by the list or lookup endpoints.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "event_id",
            "in": "query",
            "required": true,
            "description": "UUID of the decision event to prove.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The proof.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "event_content_hash": {
                      "type": "string"
                    },
                    "merkle_root": {
                      "type": "string"
                    },
                    "proof": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "directions": {
                      "type": "array",
                      "description": "Sibling position at each proof step.",
                      "items": {}
                    },
                    "leaf_index": {
                      "type": "integer"
                    },
                    "leaf_count": {
                      "type": "integer"
                    },
                    "root_anchor_signature": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Solana transaction signature anchoring the latest root, if any."
                    },
                    "root_anchor_slot": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "root_matches_published": {
                      "type": "boolean",
                      "description": "True only when the computed root equals the latest published root."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing required query parameter: event_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Investigation not found, it has no decision events, or the event is not part of it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/keys": {
      "post": {
        "operationId": "registerKey",
        "tags": [
          "Registration"
        ],
        "summary": "Register a keypair and receive a bearer token",
        "description": "Programmatic registration, no dashboard or email. Sign the message `AVOID.NET|register|<pubkey-or-address>|<unix_ts>` with a key you already hold: base58 ed25519 (Solana) or a 65-byte EIP-191 `personal_sign` signature (Ethereum). The curve is inferred from the identity. Idempotent per identity: the plaintext `api_key` is returned only on first creation (201); later calls return 200 with `api_key: null`. No endpoint currently requires the token, so registration grants no access today. Rate limit (rolling hour, default settings): 10 attempts per caller and 100 across all callers, one budget for both curves; over the limit is a 429 with Retry-After. Never cached.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "signature",
                  "timestamp"
                ],
                "description": "Provide `pubkey` or `address` (aliases; either accepts either identity).",
                "properties": {
                  "pubkey": {
                    "type": "string",
                    "description": "Base58 Solana public key (also accepts a 0x address)."
                  },
                  "address": {
                    "type": "string",
                    "description": "0x Ethereum address (alias for pubkey)."
                  },
                  "signature": {
                    "type": "string",
                    "description": "Base58 detached ed25519 signature, or 0x-prefixed 65-byte secp256k1 signature (r || s || v, v in 27/28 or 0/1)."
                  },
                  "timestamp": {
                    "type": "integer",
                    "description": "Unix seconds used in the signed message; must be within 300s of server time."
                  },
                  "scheme": {
                    "type": "string",
                    "enum": [
                      "ed25519",
                      "secp256k1"
                    ],
                    "description": "Optional. Inferred from the identity when omitted; rejected when it contradicts it."
                  },
                  "label": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Optional human-readable label."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A key already exists for this identity; `api_key` is null.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyRegistration"
                }
              }
            }
          },
          "201": {
            "description": "Key created. The plaintext key is shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyRegistration"
                }
              }
            }
          },
          "400": {
            "description": "Body is not JSON, a required field is missing or malformed, the label is invalid, or the timestamp is out of range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Well-formed request whose signature does not verify.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Registration rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Rate-limit backend unavailable (`rate_limit_unavailable`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/mcp": {
      "post": {
        "operationId": "mcp",
        "tags": [
          "MCP"
        ],
        "summary": "Model Context Protocol endpoint",
        "description": "MCP server over Streamable HTTP, stateless: each POST carries one JSON-RPC message and receives one application/json response (no SSE, no sessions; GET answers 405; JSON-RPC batching is not supported). No auth. Tools: `search_risk` (find entities by name), `lookup_entity` (resolve exact name or on-chain address), `get_investigation` (full sourced page by slug). Methods: initialize, ping, tools/list, tools/call. Discover tool schemas with `tools/list`; see the Model Context Protocol specification (https://modelcontextprotocol.io) for the wire format. Only `tools/call` is rate limited (default 30 per caller per minute, 300 across all callers); over the limit is HTTP 429 with Retry-After and a JSON-RPC error. Notifications are answered 202 with an empty body.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "const": "2.0"
                  },
                  "id": {
                    "type": [
                      "integer",
                      "string",
                      "null"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "examples": [
                      "initialize",
                      "tools/list",
                      "tools/call"
                    ]
                  },
                  "params": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response (result or error object).",
            "headers": {
              "Mcp-Protocol-Version": {
                "description": "Latest MCP protocol version this server implements.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "202": {
            "description": "Notification accepted; empty body."
          },
          "400": {
            "description": "Body was not valid JSON (JSON-RPC parse error -32700).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (JSON-RPC error code -32000).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/a2a": {
      "post": {
        "operationId": "a2a",
        "tags": [
          "A2A"
        ],
        "summary": "A2A (Agent2Agent) endpoint",
        "description": "A2A protocol 1.0 over JSON-RPC, stateless: each POST carries one JSON-RPC request and receives one application/json response. No auth. The one served method is `SendMessage`: put an entity name, an on-chain address or an avoid.net page link in a text part (or a data part with `query`, `entity`, `address` or `slug`), and the result is a `message` with a text part and a JSON data part listing the matching pages. Task and streaming methods answer -32004 (unsupported operation); an `A2A-Version` header other than 1.x answers -32009. Answers come from the same code as the MCP tools; no AI model is involved. Messages that reach the database share the MCP rate limits on a separate budget (default 30 per caller per minute, 300 across all callers); over the limit is HTTP 429 with Retry-After and a JSON-RPC error. Agent card: /.well-known/agent-card.json. Specification: https://a2a-protocol.org.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "const": "2.0"
                  },
                  "id": {
                    "type": [
                      "integer",
                      "string",
                      "null"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "examples": [
                      "SendMessage"
                    ]
                  },
                  "params": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              },
              "example": {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "SendMessage",
                "params": {
                  "message": {
                    "messageId": "1",
                    "role": "ROLE_USER",
                    "parts": [
                      {
                        "text": "Is Celsius Network safe?"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response (result with a `message`, or an error object).",
            "headers": {
              "A2A-Version": {
                "description": "A2A protocol version this agent speaks.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Body was not valid JSON (JSON-RPC parse error -32700).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (JSON-RPC error code -32000).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    },
    "/api/search": {
      "get": {
        "operationId": "search",
        "tags": [
          "Investigations"
        ],
        "summary": "Search suggestions",
        "description": "Typeahead-style search used by the site's search box, documented in /llms.txt. Matches entity name or slug, then merged-page aliases, then on-chain addresses/domains/handles; returns a short list (roughly a dozen at most). An empty `q` returns an empty list. Not part of the versioned /api/v1 surface, so its shape may change; prefer /api/v1/lookup and /api/v1/investigations for programmatic use. Edge-cached for 5 minutes.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Search text.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Suggestions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "results"
                  ],
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SearchSuggestion"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Database error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "InvestigationSummary": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "entity_name": {
            "type": "string"
          },
          "trust_score": {
            "type": [
              "number",
              "null"
            ],
            "description": "0 (high risk) to 100 (trustworthy)."
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "How complete the evidence base is."
          },
          "status": {
            "type": "string",
            "enum": [
              "published",
              "under_investigation",
              "archived",
              "merged"
            ]
          },
          "content_type": {
            "type": "string"
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "source_tags": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Intelligence feeds that contributed, e.g. `zachxbt`, `community`."
          },
          "reviewed": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the page has been fact-checked."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InvestigationDetail": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "entity_name": {
            "type": "string"
          },
          "trust_score": {
            "type": [
              "number",
              "null"
            ],
            "description": "0 (high risk) to 100 (trustworthy)."
          },
          "severity_base": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Seed score from structured content analysis."
          },
          "score_modifier": {
            "type": "number",
            "description": "Adjustment applied from evidence decisions."
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "published",
              "under_investigation",
              "archived",
              "merged"
            ]
          },
          "content_type": {
            "type": "string"
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "sections": {
            "type": [
              "array",
              "null"
            ],
            "description": "Narrative sections with per-section severity and sourced citations.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "timeline": {
            "type": [
              "array",
              "null"
            ],
            "description": "Dated events.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "sources_used": {
            "type": [
              "array",
              "null"
            ],
            "description": "Citation objects with credibility ratings.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "source_tags": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "addresses": {
            "type": [
              "array",
              "null"
            ],
            "description": "Associated blockchain addresses.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "reviewed": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "reviewed_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "model_used": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LookupMatch": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "entity_name": {
            "type": "string"
          },
          "trust_score": {
            "type": [
              "number",
              "null"
            ]
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "published",
              "under_investigation",
              "archived",
              "merged"
            ]
          },
          "content_type": {
            "type": "string"
          },
          "source_tags": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SearchSuggestion": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "entity_name": {
            "type": "string"
          },
          "trust_score": {
            "type": [
              "number",
              "null"
            ]
          },
          "match_source": {
            "type": "string",
            "enum": [
              "investigation",
              "entity",
              "merged"
            ]
          },
          "matched_entity": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "kind": {
                "type": "string"
              },
              "value": {
                "type": "string"
              }
            }
          },
          "merged_from": {
            "type": [
              "object",
              "null"
            ],
            "description": "For `merged` matches: the retired page the query matched.",
            "properties": {
              "slug": {
                "type": "string"
              },
              "entity_name": {
                "type": "string"
              }
            }
          }
        }
      },
      "KeyRegistration": {
        "type": "object",
        "properties": {
          "api_key": {
            "type": [
              "string",
              "null"
            ],
            "description": "Bearer token (`avoid_...`). Returned once, on creation only."
          },
          "tier": {
            "type": "string"
          },
          "scheme": {
            "type": "string",
            "enum": [
              "ed25519",
              "secp256k1"
            ]
          },
          "owner": {
            "type": "string",
            "description": "Display form of the registered identity."
          },
          "owner_pubkey": {
            "type": "string"
          },
          "is_new": {
            "type": "boolean"
          },
          "rate_limit_per_min": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Present on creation (201) only."
          },
          "message": {
            "type": "string",
            "description": "Present when is_new is false."
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Token from POST /api/v1/keys, sent as `Authorization: Bearer avoid_...`. Not currently required or validated by any endpoint in this document."
      }
    }
  }
}