{
  "openapi": "3.0.3",
  "info": {
    "title": "Free2AITools API",
    "version": "2.0.0",
    "description": "Structured discovery, evidence, and identity layer for AI agents, powered by the Free2AITools Nexus Index (FNI). Returns FNI-ranked catalog data and evidence for the calling agent to reason over. Discovery layer only: does not perform compatibility analysis (hardware/framework fields are stored heuristics); does not execute, plan, or recommend workflows; does not select or decide on behalf of the caller; does not currently provide live semantic/ANN ranking.",
    "contact": {
      "name": "Free2AITools",
      "url": "https://free2aitools.com/developers"
    },
    "license": {
      "name": "MIT",
      "url": "https://github.com/mosesy5688-cell/ai-nexus/blob/main/LICENSE"
    }
  },
  "servers": [
    {
      "url": "https://free2aitools.com",
      "description": "Production"
    }
  ],
  "paths": {
    "/api/v1/select": {
      "post": {
        "operationId": "selectModel",
        "summary": "Filter the catalog by declared metadata; returns FNI-ranked entries",
        "description": "Filter the Free2AITools catalog by declared metadata and return FNI-ranked entries. Constraints are metadata/heuristic filters, not verified compatibility analysis. The caller is responsible for final model selection. Each entry carries its FNI factor breakdown plus an optional factual `fni_summary` (evidence/factor facts, not a fit verdict).",
        "tags": [
          "Model Selection"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SelectRequest"
              },
              "example": {
                "task": "text-generation",
                "constraints": {
                  "max_vram_gb": 24,
                  "license": "commercial",
                  "ollama_compatible": true
                },
                "limit": 5,
                "explain": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "FNI-ranked catalog entries",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SelectResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (missing task field or invalid JSON)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Rankings data not yet available — retry after next pipeline run",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/search": {
      "get": {
        "operationId": "search",
        "summary": "Search AI models, tools, datasets, and papers",
        "description": "Full-text search across the Free2AITools catalog of 656,000+ AI models, tools, datasets, papers, and benchmarks. Search results are ordered by a relevance score based on the FNI and, where term-match data is available, how well the entry matches the query. The score used for ordering may differ from the fni_score field returned in the response. The result set is bounded. Returns up to 20 results per request. Pagination is 1-based via `page` (default page is 1; offset = (page - 1) * limit). `total_count` is the size of the bounded result set this response was drawn from. It is not the total number of matches, may vary between requests, and must not be used to compute remaining pages. Results may change between requests as the dataset is refreshed; this endpoint does not provide cursor or snapshot consistency. Search may return a retryable transient 503 under cold-path or fallback budget limits; retry according to Retry-After.",
        "tags": [
          "Search"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Search query",
            "example": "text generation model for coding"
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "model",
                "tool",
                "dataset",
                "paper",
                "benchmark"
              ]
            },
            "description": "Filter by entity type"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 5
            },
            "description": "Maximum results per request (capped at 20)"
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page; offset = (page-1)*limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Search results are ordered by a relevance score based on the FNI and, where term-match data is available, how well the entry matches the query. The score used for ordering may differ from the fni_score field returned in the response. The result set is bounded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "503": {
            "description": "Transient: a search tier (term-index, cold-shard hydration, or cluster fallback) exhausted its wall-clock/op budget. Retryable — honor the Retry-After header. Body is no-store and carries a machine-readable reason (term_index_timeout | cold_shard_timeout | cluster_fallback_budget | embedding_timeout | search_budget_exceeded). A 503 is NOT an empty result; do not treat it as 'no matches'.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string"
                },
                "description": "Seconds to wait before retrying"
              }
            }
          }
        }
      }
    },
    "/api/v1/compare": {
      "get": {
        "operationId": "compareModels",
        "summary": "Compare 2-25 AI models side-by-side",
        "description": "Returns FNI factor decomposition, specs, and metadata for each model in parallel columns. Cold upper-range multi-paper requests may return a transient 503 (retry after the indicated delay).",
        "tags": [
          "Comparison"
        ],
        "parameters": [
          {
            "name": "ids",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated model IDs (2-25)",
            "example": "meta-llama/Llama-3.1-8B,google/gemma-2-27b"
          }
        ],
        "responses": {
          "200": {
            "description": "Side-by-side comparison",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompareResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing ids parameter, fewer than 2, or more than 25",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Comparison inconclusive — transient/budget. Cold upper-range multi-paper requests may exhaust the cold-shard wall-clock budget or fan-out cap; the caller may retry (Retry-After header). Response body lists resolved vs pending ids.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/badge/{umid}": {
      "get": {
        "operationId": "getFniBadge",
        "summary": "FNI score badge (SVG)",
        "description": "Returns an SVG badge showing the entity's FNI score. Embeddable in READMEs and documentation.",
        "tags": [
          "Badge"
        ],
        "parameters": [
          {
            "name": "umid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Entity ID (slug format: author--name)",
            "example": "meta-llama--llama-3.3-70b-instruct"
          }
        ],
        "responses": {
          "200": {
            "description": "SVG badge image for an entity with a computed FNI score.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Entity not found or has no FNI score. Response body is still an SVG placeholder ('FNI: not found') so README <img> embeds render gracefully, but the 404 status lets programmatic callers (Agents) distinguish missing entities from low-scoring ones.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/entity/{id}": {
      "get": {
        "operationId": "getEntity",
        "summary": "Single-entity detail lookup",
        "description": "Returns full structured metadata for one entity. Closes the search -> detail chain so Agents don't have to scrape HTML. Tolerant of multiple ID forms: internal canonical (hf-model--author--name), HF-native (author/name), bare name, slug, or umid.",
        "tags": [
          "Entity"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Entity ID in any tolerated form",
            "example": "meta-llama/Llama-3.1-8B-Instruct"
          },
          {
            "name": "include",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated optional sections. Currently supports: body (adds readme_html, up to 250KB)",
            "example": "body"
          }
        ],
        "responses": {
          "200": {
            "description": "Entity detail grouped into identity / classification / fni / specs / stats / links / relations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EntityResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing required path parameter: id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Entity not found (all probed shards returned 0 rows successfully)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Identity ambiguity or type conflict (C4 identity-resolution parity). code=AMBIGUOUS_ENTITY_ID when a bare/untyped id maps to >1 co-resident typed record; code=IDENTITY_TYPE_CONFLICT when an exact typed canonical id's stored row type conflicts with its id prefix. Both twins remain retrievable by their exact typed id; the body lists the public {id,type} candidates (max 25) so the caller retries with ONE exact typed id. An optional candidate_overflow:true marks that the co-resident set exceeded the cap and the candidate list is a bounded window — the resolver refuses a false unique from a truncated set rather than emitting a wrong FOUND or a clean 404. No record is silently collapsed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EntityAmbiguityError"
                }
              }
            }
          },
          "503": {
            "description": "All probed shards errored — transient infrastructure issue, caller may retry. Interim: entity lookup may return transient 503 under cold-path probe budget; 404/503 contract under runtime diagnosis.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Runtime observability snapshot",
        "description": "Per-isolate VFS counters + Node runtime info. Cloudflare isolates have independent state, so each call returns metrics from the isolate that handled the request — useful for spot-checking during incidents, not global aggregation. `Cache-Control: no-store` (CDN does not cache).",
        "tags": [
          "Health"
        ],
        "responses": {
          "200": {
            "description": "Health snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/datasets": {
      "get": {
        "operationId": "listDatasets",
        "summary": "List open datasets (parquet format)",
        "description": "Returns JSON manifest of FNI-scored AI entity datasets. Pass `?file=<id>` to redirect (302) to the parquet download URL.",
        "tags": [
          "Datasets"
        ],
        "parameters": [
          {
            "name": "file",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Dataset ID — when present, server returns 302 redirect to CDN download URL",
            "example": "fni_lite_latest"
          }
        ],
        "responses": {
          "200": {
            "description": "Dataset manifest",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatasetsResponse"
                }
              }
            }
          },
          "302": {
            "description": "Redirect to dataset download URL (only when ?file= is present and recognized)"
          },
          "404": {
            "description": "Unknown file ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/concepts": {
      "get": {
        "operationId": "listConcepts",
        "summary": "List knowledge articles (concepts)",
        "description": "Layer 0 endpoint. Returns a paginated list of published knowledge articles (concepts) from the anchor knowledge DB, ranked newest-first (published_at DESC, id ASC). Heavy content/highlights columns are excluded from the list payload; per-article detail goes through /api/v1/entity/<id>. Read-only, unauthenticated.",
        "tags": [
          "Concepts"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            },
            "description": "Max articles to return. Clamped to [1, 200]; non-numeric values fall back to 50.",
            "example": 50
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Number of articles to skip for pagination. Floored at 0; non-numeric values fall back to 0.",
            "example": 0
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{0,40}$"
            },
            "description": "Optional category filter. Must match ^[a-z][a-z0-9-]{0,40}$ (lowercase, starts with a letter, ≤41 chars) or the request is rejected with 400.",
            "example": "machine-learning"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of published knowledge articles. `next_offset` is null when the final page has been reached; `category` echoes the applied filter (null when none).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConceptsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid category format (did not match ^[a-z][a-z0-9-]{0,40}$). Body is the structured concepts error envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConceptsError"
                }
              }
            }
          },
          "500": {
            "description": "Failed to load the knowledge index (upstream/DB error). Body is the structured concepts error envelope with code UPSTREAM_ERROR.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConceptsError"
                }
              }
            }
          }
        }
      },
      "options": {
        "operationId": "conceptsPreflight",
        "summary": "CORS preflight",
        "description": "CORS preflight. Returns 204 No Content with the CORS headers (Allow-Methods: GET, OPTIONS).",
        "tags": [
          "Concepts"
        ],
        "responses": {
          "204": {
            "description": "No Content — CORS preflight acknowledged."
          }
        }
      }
    },
    "/api/v1/trends/batch": {
      "get": {
        "operationId": "trendsBatch",
        "summary": "Batch 7-day FNI trend lookup",
        "description": "Layer 0 endpoint. Returns the 7-day FNI trend (scores, dates, 7-day change %, direction, and latest score) for up to 25 entities in a single call. Lookup is exact-match by entity id; ids with no trend data are returned in `missing` (never an error). Read-only, unauthenticated.",
        "tags": [
          "Trends"
        ],
        "parameters": [
          {
            "name": "ids",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated entity IDs (1-25). Whitespace is trimmed and empty tokens are dropped before counting.",
            "example": "meta-llama/Llama-3.1-8B-Instruct,google/gemma-2-27b"
          }
        ],
        "responses": {
          "200": {
            "description": "Trend entries keyed by entity id. `missing` lists requested ids that had no trend data. When the trend index is unavailable, all requested ids appear in `missing` and `trends` is empty (still 200).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrendsBatchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing ids parameter, zero ids after trimming, or more than 25 ids. Body is the simple {error} envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Internal error while loading or decoding the trend index. Body is the simple {error} envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "options": {
        "operationId": "trendsBatchPreflight",
        "summary": "CORS preflight",
        "description": "CORS preflight. Returns 204 No Content with the CORS headers (Allow-Methods: GET, OPTIONS).",
        "tags": [
          "Trends"
        ],
        "responses": {
          "204": {
            "description": "No Content — CORS preflight acknowledged."
          }
        }
      }
    },
    "/api/mcp": {
      "post": {
        "operationId": "mcpEndpoint",
        "summary": "MCP Server (JSON-RPC 2.0)",
        "description": "Model Context Protocol endpoint. Supports tools/list and tools/call for 5 tools: free2aitools_search, free2aitools_rank, free2aitools_explain, free2aitools_select_model, free2aitools_compare. Tool names MUST include the free2aitools_ prefix when invoking tools/call. **For the canonical tool catalog with full input schemas, fetch `/.well-known/mcp.json`** — that file is the canonical source. This OpenAPI entry intentionally does not duplicate it (DRY).",
        "tags": [
          "MCP"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "enum": [
                      "2.0"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "initialize",
                      "tools/list",
                      "tools/call"
                    ]
                  },
                  "id": {
                    "type": "integer"
                  },
                  "params": {
                    "type": "object"
                  }
                },
                "required": [
                  "jsonrpc",
                  "method",
                  "id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC 2.0 response"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "SelectRequest": {
        "type": "object",
        "required": [
          "task"
        ],
        "properties": {
          "task": {
            "type": "string",
            "description": "Task name or natural language description",
            "example": "text-generation"
          },
          "constraints": {
            "type": "object",
            "properties": {
              "max_vram_gb": {
                "type": "number",
                "description": "Maximum VRAM in GB"
              },
              "max_params_b": {
                "type": "number",
                "description": "Maximum parameters in billions"
              },
              "license": {
                "type": "string",
                "description": "'commercial', 'apache-2.0', 'mit', or 'any'"
              },
              "min_context_length": {
                "type": "integer",
                "description": "Minimum context window tokens"
              },
              "ollama_compatible": {
                "type": "boolean",
                "description": "Only GGUF/Ollama-compatible models"
              },
              "can_run_local": {
                "type": "boolean",
                "description": "Locally runnable (≤13B + GGUF)"
              },
              "hosted_on": {
                "type": "string",
                "description": "Hosting provider: replicate, together, hf-inference"
              },
              "license_type": {
                "type": "string",
                "enum": [
                  "permissive",
                  "copyleft",
                  "non-commercial",
                  "any"
                ]
              }
            }
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 5
          },
          "explain": {
            "type": "boolean",
            "default": true,
            "description": "Include per-entry fni_summary and caveats"
          }
        }
      },
      "SelectResponse": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "example": "fni_v2.0"
          },
          "task_interpreted": {
            "type": "string"
          },
          "total_candidates": {
            "type": "integer"
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Entry"
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "elapsed_ms": {
                "type": "integer"
              }
            }
          }
        }
      },
      "Entry": {
        "type": "object",
        "properties": {
          "rank": {
            "type": "integer"
          },
          "model_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "author": {
            "type": "string"
          },
          "fni_score": {
            "type": "number"
          },
          "fni_factors": {
            "type": "object",
            "properties": {
              "semantic": {
                "type": "number",
                "nullable": true
              },
              "semantic_note": {
                "type": "string"
              },
              "authority": {
                "type": "number"
              },
              "popularity": {
                "type": "number"
              },
              "recency": {
                "type": "number"
              },
              "quality": {
                "type": "number"
              }
            }
          },
          "params_billions": {
            "type": "number",
            "nullable": true
          },
          "vram_estimate_gb": {
            "type": "number",
            "nullable": true
          },
          "context_length": {
            "type": "integer",
            "nullable": true
          },
          "license": {
            "type": "string",
            "nullable": true
          },
          "pipeline_tag": {
            "type": "string",
            "nullable": true
          },
          "ollama_compatible": {
            "type": "boolean"
          },
          "hosted_on": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "license_type": {
            "type": "string"
          },
          "can_run_local": {
            "type": "boolean"
          },
          "fni_summary": {
            "type": "string",
            "description": "Factual FNI factor/spec summary of this catalog entry — not a fit verdict"
          },
          "caveats": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "detail_url": {
            "type": "string",
            "format": "uri"
          },
          "badge_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "SearchResponse": {
        "type": "object",
        "description": "Public /api/v1/search 200 response. Fields are the internal SSR search row (DISPLAY_COLS) wrapped by the v1 versioner: `version` is prepended, `fni_s` is nulled (+ `fni_s_note`), and the internal `_dbSort`/`_score`/`_source` fields are stripped before serialization.",
        "required": [
          "version",
          "results",
          "total_count",
          "tier",
          "elapsed_ms"
        ],
        "properties": {
          "version": {
            "type": "string",
            "description": "API version tag added by the v1 wrapper (e.g. fni_v2.0). Always present on a 200."
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "One catalog entity (SSR DISPLAY_COLS, v1-transformed). Non-measured numeric/string stats surface as JSON null (honest-contract), not 0 or empty.",
              "properties": {
                "id": {
                  "type": "string"
                },
                "slug": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "description": "model | tool | dataset | paper | benchmark"
                },
                "author": {
                  "type": "string",
                  "nullable": true
                },
                "summary": {
                  "type": "string",
                  "nullable": true
                },
                "fni_score": {
                  "type": "number",
                  "nullable": true
                },
                "fni_s": {
                  "type": "number",
                  "nullable": true,
                  "description": "Always null in the v1 baseline: the Semantic pillar is a query-time baseline, not a per-entity measurement, and live semantic/ANN ranking is not currently provided. See fni_s_note."
                },
                "fni_s_note": {
                  "type": "string",
                  "description": "Present alongside fni_s: explains it is a query-time baseline, not a per-entity value."
                },
                "fni_a": {
                  "type": "number",
                  "nullable": true,
                  "description": "Authority factor."
                },
                "fni_p": {
                  "type": "number",
                  "nullable": true,
                  "description": "Popularity factor."
                },
                "fni_r": {
                  "type": "number",
                  "nullable": true,
                  "description": "Recency factor."
                },
                "fni_q": {
                  "type": "number",
                  "nullable": true,
                  "description": "Quality factor."
                },
                "stars": {
                  "type": "integer",
                  "nullable": true
                },
                "downloads": {
                  "type": "integer",
                  "nullable": true
                },
                "last_modified": {
                  "type": "string",
                  "nullable": true,
                  "format": "date-time"
                },
                "license": {
                  "type": "string",
                  "nullable": true
                },
                "pipeline_tag": {
                  "type": "string",
                  "nullable": true
                },
                "params_billions": {
                  "type": "number",
                  "nullable": true
                },
                "context_length": {
                  "type": "integer",
                  "nullable": true
                }
              }
            }
          },
          "total_count": {
            "type": "integer",
            "description": "`total_count` is the size of the bounded result set this response was drawn from. It is not the total number of matches, may vary between requests, and must not be used to compute remaining pages. Always present on a 200."
          },
          "tier": {
            "type": "string",
            "description": "Which search tier produced the results (e.g. inverted_index | cluster_fallback | browse | empty)."
          },
          "elapsed_ms": {
            "type": "integer"
          }
        }
      },
      "CompareResponse": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "example": "fni_v2.0"
          },
          "entities": {
            "type": "array",
            "description": "Per-ID result in request order. Unresolved IDs are returned as `{id, found: false}` placeholders — the overall response remains 200; the caller inspects `found` to detect partial misses.",
            "items": {
              "oneOf": [
                {
                  "type": "object",
                  "description": "Resolved entity",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "author": {
                      "type": "string",
                      "nullable": true
                    },
                    "type": {
                      "type": "string"
                    },
                    "fni_score": {
                      "type": "number"
                    },
                    "fni_factors": {
                      "type": "object",
                      "properties": {
                        "semantic": {
                          "type": "number",
                          "nullable": true
                        },
                        "authority": {
                          "type": "number"
                        },
                        "popularity": {
                          "type": "number"
                        },
                        "recency": {
                          "type": "number"
                        },
                        "quality": {
                          "type": "number"
                        }
                      }
                    },
                    "specs": {
                      "type": "object",
                      "properties": {
                        "params_billions": {
                          "type": "number",
                          "nullable": true
                        },
                        "context_length": {
                          "type": "integer",
                          "nullable": true
                        },
                        "vram_estimate_gb": {
                          "type": "number",
                          "nullable": true
                        },
                        "license": {
                          "type": "string",
                          "nullable": true
                        },
                        "architecture": {
                          "type": "string",
                          "nullable": true
                        },
                        "pipeline_tag": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "popularity": {
                      "type": "object",
                      "properties": {
                        "downloads": {
                          "type": "integer"
                        },
                        "stars": {
                          "type": "integer"
                        }
                      }
                    },
                    "last_modified": {
                      "type": "string",
                      "nullable": true,
                      "format": "date-time"
                    },
                    "detail_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "badge_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "found": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  }
                },
                {
                  "type": "object",
                  "description": "Unresolved ID — placeholder when the entity could not be located in any meta shard",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "found": {
                      "type": "boolean",
                      "enum": [
                        false
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "found"
                  ]
                },
                {
                  "type": "object",
                  "description": "Ambiguous bare/untyped ID (C4 identity-resolution parity) — maps to >1 co-resident typed record. ADDITIVE to the unresolved shape (still found:false, and the overall response stays HTTP 200): the caller retries with ONE exact typed canonical id from `candidates`. Exact typed ids still each resolve to their own twin. `candidate_overflow` (optional, only when true) means the co-resident set exceeded the cap so `candidates` is a bounded window (max 25).",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "found": {
                      "type": "boolean",
                      "enum": [
                        false
                      ]
                    },
                    "ambiguous": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "AMBIGUOUS_ENTITY_ID"
                      ]
                    },
                    "candidates": {
                      "type": "array",
                      "maxItems": 25,
                      "items": {
                        "$ref": "#/components/schemas/AmbiguityCandidate"
                      }
                    },
                    "candidate_overflow": {
                      "type": "boolean",
                      "enum": [
                        true
                      ],
                      "description": "Optional; true ONLY when the co-resident candidate set exceeded the public cap of 25 (bounded, possibly non-exhaustive window)."
                    }
                  },
                  "required": [
                    "id",
                    "found",
                    "ambiguous",
                    "code",
                    "candidates"
                  ]
                },
                {
                  "type": "object",
                  "description": "Identity type-conflict for one compare ID (C4 identity-resolution parity) — the requested id's typed prefix conflicts with the stored record's type. NOT a clean miss and NOT a success: found:false with code=IDENTITY_TYPE_CONFLICT and the single stored canonical {id,type} in `candidates` (NO `ambiguous` flag — only one record is proven). The overall response stays HTTP 200; the caller corrects the typed identifier or retries with the listed candidate.",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "found": {
                      "type": "boolean",
                      "enum": [
                        false
                      ]
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "IDENTITY_TYPE_CONFLICT"
                      ]
                    },
                    "candidates": {
                      "type": "array",
                      "maxItems": 25,
                      "items": {
                        "$ref": "#/components/schemas/AmbiguityCandidate"
                      }
                    }
                  },
                  "required": [
                    "id",
                    "found",
                    "code",
                    "candidates"
                  ]
                }
              ]
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "elapsed_ms": {
                "type": "integer"
              },
              "found": {
                "type": "integer",
                "description": "Count of entities with found: true"
              },
              "requested": {
                "type": "integer",
                "description": "Total IDs in the request"
              }
            }
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "example": "fni_v2.0"
          },
          "status": {
            "type": "string",
            "example": "ok"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "served_build_id": {
            "type": "string",
            "nullable": true,
            "description": "P1b freshness signal: build_id of the served shards manifest for this cycle (null when the manifest carries no build_id token). The external reliability probe compares this against the public id-index header build_id for index<->manifest coherence.",
            "example": "run-29497071238"
          },
          "manifest_etag": {
            "type": "string",
            "nullable": true,
            "description": "P1b: ETag of the served shards manifest (null if unavailable)."
          },
          "manifest_state": {
            "type": "string",
            "enum": [
              "loaded",
              "fallback",
              "unavailable"
            ],
            "description": "P1b: manifest load state — 'loaded' (real manifest resolved), 'fallback' (loadManifest hit its fallback path), or 'unavailable' (manifest could not be resolved). Sourced from the already-loaded small manifest; Health never force-loads the ~26MB id-index."
          },
          "reader": {
            "type": "object",
            "description": "R5 Phase-1 reader substrate signal (Founder D-2026-0717-350). Publication metadata ONLY — reader_mode, publication_source, build_id, generation. Contains NO raw request counts, user traffic, or adoption/KPI (any path-selection counter is kept internal). Production is fenced to legacy_only; the pointer path is unreachable in production.",
            "properties": {
              "reader_mode": {
                "type": "string",
                "enum": [
                  "legacy_only",
                  "pointer_capable"
                ],
                "description": "The reader mode this isolate resolved with. Production is hardcoded to 'legacy_only' (the fence); 'pointer_capable' is reachable only via test dependency injection."
              },
              "publication_source": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "pointer",
                  "legacy",
                  "fallback"
                ],
                "description": "Which serving path produced the pin: 'legacy' (fixed-key shards_manifest.json, the production path in Phase 1), 'fallback' (loadManifest hard-fallback), or 'pointer' (atomic published pointer — not reachable in production Phase 1). null when the manifest could not be resolved."
              },
              "build_id": {
                "type": "string",
                "nullable": true,
                "description": "build_id of the resolved cycle pin (null when unresolved)."
              },
              "generation": {
                "type": "integer",
                "nullable": true,
                "description": "Monotonic pointer generation. null in legacy_only (no pointer generation exists on the legacy path)."
              }
            }
          },
          "vfs": {
            "type": "object",
            "description": "Per-isolate R2 VFS counters. Cache hit ratios are derived client-side: l0_hit_ratio = l0_hits / jread_total; l1_hit_ratio = l1_hits / (jread_total - l0_hits); short_read_rate = short_read_attempts / l2_fetches.",
            "properties": {
              "jread_total": {
                "type": "integer",
                "description": "Total VFS reads attempted since isolate boot"
              },
              "jread_errors": {
                "type": "integer"
              },
              "short_read_attempts": {
                "type": "integer"
              },
              "short_read_recovered": {
                "type": "integer"
              },
              "short_read_exhausted": {
                "type": "integer"
              },
              "l0_hits": {
                "type": "integer",
                "description": "Chunks served from in-memory L0 cache"
              },
              "l1_hits": {
                "type": "integer",
                "description": "Chunks served from Cloudflare Cache API"
              },
              "l2_fetches": {
                "type": "integer",
                "description": "Chunks fetched from R2 origin or CDN fallback"
              },
              "l0_size": {
                "type": "integer"
              },
              "l0_max": {
                "type": "integer"
              },
              "isolate_uptime_ms": {
                "type": "integer"
              },
              "error": {
                "type": "string",
                "description": "Present only when VFS module is not initialized in this isolate"
              }
            }
          },
          "runtime": {
            "type": "object",
            "properties": {
              "node_compat": {
                "type": "boolean"
              },
              "now_ms": {
                "type": "integer"
              }
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "elapsed_ms": {
                "type": "integer"
              },
              "isolate_random_id": {
                "type": "string",
                "description": "8-char random ID — different per Cloudflare isolate, useful for grouping spot-check requests"
              }
            }
          }
        }
      },
      "DatasetsResponse": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "example": "fni_v2.0"
          },
          "description": {
            "type": "string"
          },
          "files": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "access"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "example": "fni_lite_latest"
                },
                "name": {
                  "type": "string"
                },
                "tier": {
                  "type": "string",
                  "enum": [
                    "free"
                  ],
                  "deprecated": true,
                  "description": "DEPRECATED legacy compatibility field. All Free2AITools datasets are open/public access; the only legal value is \"free\". Use `access` instead. Retained for backward compatibility; may be removed in a future major API version."
                },
                "access": {
                  "type": "string",
                  "enum": [
                    "public"
                  ],
                  "description": "Access class for the dataset. All current Free2AITools datasets are publicly downloadable with no auth; value is always \"public\"."
                },
                "fields": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Schema field names available in the parquet file"
                },
                "download_url": {
                  "type": "string",
                  "format": "uri",
                  "description": "Direct CDN URL for parquet file"
                },
                "api_url": {
                  "type": "string",
                  "description": "Relative API URL that returns 302 redirect to download_url"
                }
              }
            }
          }
        }
      },
      "EntityResponse": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "example": "fni_v2.0"
          },
          "entity": {
            "type": "object",
            "required": [
              "id",
              "canonical_id"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Current canonical served identifier (lookup key). Reuse verbatim."
              },
              "canonical_id": {
                "type": "string",
                "description": "The same canonical identifier value as `id` (canonical_id == id). A separate derived UMID = SHA256(canonical_id)[:16] is not emitted at the top level of this response."
              },
              "slug": {
                "type": "string"
              },
              "type": {
                "type": "string",
                "description": "model | dataset | paper | tool | benchmark | knowledge"
              },
              "name": {
                "type": "string"
              },
              "author": {
                "type": "string",
                "nullable": true
              },
              "source": {
                "type": "string",
                "nullable": true,
                "description": "huggingface | github | arxiv | replicate | etc."
              },
              "summary": {
                "type": "string",
                "nullable": true
              },
              "category": {
                "type": "string",
                "nullable": true
              },
              "tags": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "license": {
                "type": "string",
                "nullable": true
              },
              "license_type": {
                "type": "string",
                "nullable": true
              },
              "pipeline_tag": {
                "type": "string",
                "nullable": true
              },
              "task_categories": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "primary_language": {
                "type": "string",
                "nullable": true
              },
              "fni": {
                "type": "object",
                "properties": {
                  "score": {
                    "type": "number",
                    "nullable": true
                  },
                  "percentile": {
                    "type": "string",
                    "nullable": true
                  },
                  "factors": {
                    "type": "object",
                    "properties": {
                      "semantic": {
                        "type": "number",
                        "nullable": true,
                        "description": "Always null: the Semantic pillar is a query-time baseline scored live at search, not a measured per-entity value. See semantic_note."
                      },
                      "semantic_note": {
                        "type": "string",
                        "description": "Explains that semantic is a query-time baseline, not a per-entity score."
                      },
                      "authority": {
                        "type": "number",
                        "nullable": true
                      },
                      "popularity": {
                        "type": "number",
                        "nullable": true
                      },
                      "recency": {
                        "type": "number",
                        "nullable": true
                      },
                      "quality": {
                        "type": "number",
                        "nullable": true
                      }
                    }
                  },
                  "is_trending": {
                    "type": "boolean"
                  },
                  "trend_7d": {
                    "type": "string",
                    "nullable": true,
                    "description": "Comma-separated FNI scores for last 7 days, or null"
                  }
                }
              },
              "specs": {
                "type": "object",
                "properties": {
                  "params_billions": {
                    "type": "number",
                    "nullable": true
                  },
                  "context_length": {
                    "type": "integer",
                    "nullable": true
                  },
                  "architecture": {
                    "type": "string",
                    "nullable": true
                  },
                  "vocab_size": {
                    "type": "integer",
                    "nullable": true
                  },
                  "num_layers": {
                    "type": "integer",
                    "nullable": true
                  },
                  "hidden_size": {
                    "type": "integer",
                    "nullable": true
                  },
                  "vram": {
                    "type": "object",
                    "properties": {
                      "estimate_gb": {
                        "type": "number",
                        "nullable": true
                      },
                      "fp16_gb": {
                        "type": "number",
                        "nullable": true
                      },
                      "int8_gb": {
                        "type": "number",
                        "nullable": true
                      },
                      "int4_gb": {
                        "type": "number",
                        "nullable": true
                      }
                    }
                  },
                  "ollama_compatible": {
                    "type": "boolean",
                    "nullable": true
                  },
                  "can_run_local": {
                    "type": "boolean",
                    "nullable": true
                  },
                  "hosted_on": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "nullable": true
                  },
                  "runtime_hardware": {
                    "type": "string",
                    "nullable": true
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "downloads": {
                    "type": "integer",
                    "nullable": true
                  },
                  "stars": {
                    "type": "integer",
                    "nullable": true
                  },
                  "forks": {
                    "type": "integer",
                    "nullable": true
                  },
                  "citation_count": {
                    "type": "integer",
                    "nullable": true
                  },
                  "num_rows": {
                    "type": "integer",
                    "nullable": true
                  },
                  "last_modified": {
                    "type": "string",
                    "nullable": true,
                    "format": "date-time"
                  }
                }
              },
              "links": {
                "type": "object",
                "properties": {
                  "source_url": {
                    "type": "string",
                    "nullable": true,
                    "format": "uri"
                  },
                  "canonical_url": {
                    "type": "string",
                    "nullable": true,
                    "format": "uri"
                  },
                  "image_url": {
                    "type": "string",
                    "nullable": true,
                    "format": "uri"
                  },
                  "detail_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "badge_url": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              },
              "relations": {
                "type": "object",
                "properties": {
                  "datasets_used": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "benchmarks": {
                    "type": "object",
                    "nullable": true
                  },
                  "related": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  }
                }
              },
              "citation": {
                "type": "string",
                "nullable": true
              },
              "quick_start": {
                "type": "string",
                "nullable": true
              },
              "body": {
                "type": "object",
                "description": "Only present when ?include=body",
                "properties": {
                  "readme_html": {
                    "type": "string",
                    "nullable": true
                  },
                  "has_fulltext": {
                    "type": "boolean"
                  }
                }
              }
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "elapsed_ms": {
                "type": "integer"
              },
              "etag": {
                "type": "string",
                "nullable": true
              },
              "candidates_tried": {
                "type": "integer"
              }
            }
          }
        }
      },
      "Concept": {
        "type": "object",
        "description": "A single knowledge article (concept) list entry. Heavy content/highlights columns are excluded from the list payload.",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "umid": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string"
          },
          "subtitle": {
            "type": "string",
            "nullable": true
          },
          "summary": {
            "type": "string",
            "nullable": true
          },
          "category": {
            "type": "string",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "author": {
            "type": "string",
            "nullable": true
          },
          "word_count": {
            "type": "integer",
            "description": "0 when not recorded."
          },
          "published_at": {
            "type": "string",
            "nullable": true
          },
          "updated_at": {
            "type": "string",
            "nullable": true
          },
          "canonical_url": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "id",
          "slug",
          "title",
          "tags",
          "word_count"
        ]
      },
      "ConceptsResponse": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "example": "knowledge_v1"
          },
          "total_count": {
            "type": "integer",
            "description": "Total published articles matching the filter (before pagination)."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "next_offset": {
            "type": "integer",
            "nullable": true,
            "description": "Offset for the next page, or null when the final page has been reached."
          },
          "category": {
            "type": "string",
            "nullable": true,
            "description": "Echoes the applied category filter; null when none was supplied."
          },
          "concepts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Concept"
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "elapsed_ms": {
                "type": "integer"
              },
              "etag": {
                "type": "string",
                "nullable": true
              }
            }
          }
        },
        "required": [
          "version",
          "total_count",
          "limit",
          "offset",
          "next_offset",
          "category",
          "concepts",
          "meta"
        ]
      },
      "ConceptsError": {
        "type": "object",
        "description": "Structured error envelope returned by /api/v1/concepts (distinct from the simple {error} envelope).",
        "properties": {
          "error": {
            "type": "boolean",
            "example": true
          },
          "code": {
            "type": "string",
            "example": "BAD_REQUEST"
          },
          "message": {
            "type": "string"
          },
          "endpoint": {
            "type": "string",
            "example": "/api/v1/concepts"
          },
          "timestamp": {
            "type": "integer",
            "description": "Unix epoch milliseconds."
          },
          "_gateway_trace": {
            "type": "string"
          }
        },
        "required": [
          "error",
          "code",
          "message",
          "endpoint",
          "timestamp"
        ]
      },
      "TrendEntry": {
        "type": "object",
        "description": "7-day FNI trend for one entity.",
        "properties": {
          "scores": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "description": "Up to 7 daily FNI scores, oldest-first."
          },
          "dates": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Up to 7 ISO dates aligned with scores."
          },
          "change7d": {
            "type": "number",
            "description": "7-day change percentage."
          },
          "direction": {
            "type": "string",
            "enum": [
              "up",
              "down",
              "stable"
            ]
          },
          "latest": {
            "type": "number",
            "description": "Most recent FNI score."
          }
        },
        "required": [
          "scores",
          "dates",
          "change7d",
          "direction",
          "latest"
        ]
      },
      "TrendsBatchResponse": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "example": "fni_v2.0"
          },
          "trends": {
            "type": "object",
            "description": "Map of entity id → trend entry. Only ids with trend data are present.",
            "additionalProperties": {
              "$ref": "#/components/schemas/TrendEntry"
            }
          },
          "missing": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Requested ids that had no trend data."
          },
          "meta": {
            "type": "object",
            "properties": {
              "elapsed_ms": {
                "type": "integer"
              },
              "found": {
                "type": "integer",
                "description": "Count of ids resolved to a trend entry."
              },
              "requested": {
                "type": "integer",
                "description": "Count of ids requested."
              }
            }
          }
        },
        "required": [
          "version",
          "trends",
          "missing",
          "meta"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "AmbiguityCandidate": {
        "type": "object",
        "description": "Public candidate identity for an ambiguous/conflicting lookup — ONLY the canonical id and its type (no shard/rowid/internal fields).",
        "properties": {
          "id": {
            "type": "string",
            "description": "Exact typed canonical id to retry with"
          },
          "type": {
            "type": "string",
            "description": "The record's entity type (model / dataset / paper / tool / benchmark / ...)"
          }
        },
        "required": [
          "id",
          "type"
        ]
      },
      "EntityAmbiguityError": {
        "type": "object",
        "description": "GET /api/v1/entity 409 body (C4 identity-resolution parity). Returned when a bare/untyped id maps to >1 co-resident typed record (code=AMBIGUOUS_ENTITY_ID) or an exact typed canonical id's stored row type conflicts with its id prefix (code=IDENTITY_TYPE_CONFLICT). Both twins remain retrievable by their exact typed id; nothing is silently collapsed. `candidate_overflow` (optional, only present when true) means the co-resident set exceeded the candidate cap, so `candidates` is a bounded window (max 25) — retry with ONE exact typed id.",
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable summary (e.g. \"Ambiguous entity identifier\" / \"Entity identifier type conflict\")"
          },
          "code": {
            "type": "string",
            "enum": [
              "AMBIGUOUS_ENTITY_ID",
              "IDENTITY_TYPE_CONFLICT"
            ]
          },
          "requested_id": {
            "type": "string",
            "description": "The id exactly as requested"
          },
          "candidates": {
            "type": "array",
            "maxItems": 25,
            "items": {
              "$ref": "#/components/schemas/AmbiguityCandidate"
            }
          },
          "candidate_overflow": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Optional; present and true ONLY when the co-resident candidate set exceeded the public cap of 25, so `candidates` is a bounded (possibly non-exhaustive) window. The resolver refuses a false unique from a truncated set; retry with ONE exact typed canonical id."
          }
        },
        "required": [
          "error",
          "code",
          "requested_id",
          "candidates"
        ]
      }
    }
  }
}