{
  "openapi": "3.1.0",
  "info": {
    "title": "AgentFriendlyRank API",
    "version": "1.0.0",
    "summary": "Agent readiness scores, reports and rankings, read-only.",
    "description": "Every score is produced by the published rubric (currently v1.0.1, https://agentfriendlyrank.com/methodology) from an unauthenticated crawl, and every check carries the evidence captured during that crawl.\n\nThe API is read-only and needs no key. To have a site scanned, submit the form at /scan (it works without JavaScript and answers with a redirect to /scan/{id}), then poll /scans/{id} until status is \"complete\".\n\nAn MCP server exposing the same data as tools is at https://agentfriendlyrank.com/mcp. Human-readable documentation: https://agentfriendlyrank.com/docs.",
    "termsOfService": "https://agentfriendlyrank.com/terms",
    "contact": {
      "name": "Socio360",
      "email": "hello@agentfriendlyrank.com",
      "url": "https://agentfriendlyrank.com/contact"
    }
  },
  "servers": [
    {
      "url": "https://agentfriendlyrank.com/api/v1"
    }
  ],
  "tags": [
    {
      "name": "reports",
      "description": "Per-site reports and comparisons."
    },
    {
      "name": "leaderboard",
      "description": "Ranked listings of every published site."
    },
    {
      "name": "rubric",
      "description": "The scoring rubric itself."
    },
    {
      "name": "scans",
      "description": "Progress of a scan in flight."
    }
  ],
  "paths": {
    "/": {
      "get": {
        "operationId": "getIndex",
        "summary": "API index",
        "description": "Links to every endpoint, the OpenAPI document and the MCP server.",
        "responses": {
          "200": {
            "description": "Index",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/rubric": {
      "get": {
        "operationId": "getRubric",
        "tags": [
          "rubric"
        ],
        "summary": "The published rubric",
        "description": "Four pillars, 27 checks, point weights, grade bands and the version changelog — the same definition that scores every report.",
        "responses": {
          "200": {
            "description": "The rubric",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Rubric"
                }
              }
            }
          }
        }
      }
    },
    "/leaderboard": {
      "get": {
        "operationId": "getLeaderboard",
        "tags": [
          "leaderboard"
        ],
        "summary": "Ranked list of published sites",
        "description": "Every published site with its latest completed scan, highest score first. Hidden sites and robots.txt opt-outs never appear.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "ecommerce",
                "saas",
                "content",
                "other"
              ]
            },
            "description": "Restrict to one site type."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The ranking",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Leaderboard"
                }
              }
            }
          },
          "400": {
            "description": "Unknown category or malformed paging",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Scan database unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/sites/{domain}": {
      "get": {
        "operationId": "getSiteReport",
        "tags": [
          "reports"
        ],
        "summary": "Full report for one domain",
        "description": "The latest completed scan: total, grade, pillar scores, the five fixes the engine ranks first, and all 27 checks with their captured evidence. Any URL or hostname form is accepted and normalised to the bare domain.",
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "agentfriendlyrank.com"
          }
        ],
        "responses": {
          "200": {
            "description": "The report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteReport"
                }
              }
            }
          },
          "400": {
            "description": "Not a public domain",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No published report. Never scanned, hidden at the owner's request, or opted out via robots.txt — the detail says which of the last two applies only for robots.txt.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Scan database unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/compare/{a}/{b}": {
      "get": {
        "operationId": "compareSites",
        "tags": [
          "reports"
        ],
        "summary": "Two sites side by side",
        "description": "Summaries of both sites plus every check with each side's status and points. Order does not matter; the response is in canonical (alphabetical) order.",
        "parameters": [
          {
            "name": "a",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "b",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The comparison",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Comparison"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or identical domains",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "One or both sites have no published report; the detail names them",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Scan database unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/scans/{id}": {
      "get": {
        "operationId": "getScanStatus",
        "tags": [
          "scans"
        ],
        "summary": "Status of a scan",
        "description": "For polling after submitting /scan. Statuses: queued, crawling, scoring, complete, failed, blocked (robots.txt disallows our crawler). reportUrl is set once complete.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScanStatus"
                }
              }
            }
          },
          "404": {
            "description": "Unknown scan id",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Scan database unavailable",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details.",
        "required": [
          "type",
          "title",
          "status"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string"
          }
        }
      },
      "PillarScores": {
        "type": "object",
        "description": "Points out of 25 per pillar: A discovery & access, B understanding, C actionability, D trust & freshness.",
        "required": [
          "A",
          "B",
          "C",
          "D"
        ],
        "properties": {
          "A": {
            "type": "number"
          },
          "B": {
            "type": "number"
          },
          "C": {
            "type": "number"
          },
          "D": {
            "type": "number"
          }
        }
      },
      "ScanSummary": {
        "type": "object",
        "required": [
          "id",
          "scoreTotal",
          "grade",
          "pillars",
          "siteType",
          "rubricVersion",
          "finishedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "scoreTotal": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "grade": {
            "type": "string",
            "enum": [
              "A+",
              "A",
              "B",
              "C",
              "D",
              "F"
            ]
          },
          "pillars": {
            "$ref": "#/components/schemas/PillarScores"
          },
          "siteType": {
            "type": "string",
            "enum": [
              "ecommerce",
              "saas",
              "content",
              "other"
            ]
          },
          "rubricVersion": {
            "type": "string"
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SiteSummary": {
        "type": "object",
        "required": [
          "domain",
          "reportUrl",
          "badgeUrl",
          "scan"
        ],
        "properties": {
          "domain": {
            "type": "string"
          },
          "reportUrl": {
            "type": "string",
            "format": "uri"
          },
          "badgeUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Set for listed sites at grade B or better."
          },
          "scan": {
            "$ref": "#/components/schemas/ScanSummary"
          }
        }
      },
      "Check": {
        "type": "object",
        "required": [
          "id",
          "pillar",
          "name",
          "status",
          "pointsEarned",
          "pointsPossible",
          "recommendation",
          "impact",
          "effort",
          "evidence"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "A2"
          },
          "pillar": {
            "type": "string",
            "enum": [
              "A",
              "B",
              "C",
              "D"
            ]
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pass",
              "partial",
              "fail",
              "na"
            ]
          },
          "pointsEarned": {
            "type": "number"
          },
          "pointsPossible": {
            "type": "number"
          },
          "recommendation": {
            "type": "string"
          },
          "impact": {
            "type": "string",
            "enum": [
              "High",
              "Medium",
              "Low"
            ]
          },
          "effort": {
            "type": "string",
            "enum": [
              "High",
              "Medium",
              "Low"
            ]
          },
          "evidence": {
            "type": "object",
            "description": "What the crawl captured for this check. Shape varies per check.",
            "additionalProperties": true
          }
        }
      },
      "Fix": {
        "type": "object",
        "required": [
          "id",
          "name",
          "impact",
          "effort",
          "recommendation"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "impact": {
            "type": "string"
          },
          "effort": {
            "type": "string"
          },
          "recommendation": {
            "type": "string"
          }
        }
      },
      "SiteReport": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SiteSummary"
          },
          {
            "type": "object",
            "required": [
              "topFixes",
              "checks"
            ],
            "properties": {
              "topFixes": {
                "type": "array",
                "description": "Up to five failing or partial checks, ranked by impact then effort.",
                "items": {
                  "$ref": "#/components/schemas/Fix"
                }
              },
              "checks": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Check"
                }
              }
            }
          }
        ]
      },
      "LeaderboardEntry": {
        "type": "object",
        "required": [
          "rank",
          "domain",
          "category",
          "scoreTotal",
          "grade",
          "rubricVersion",
          "finishedAt",
          "reportUrl"
        ],
        "properties": {
          "rank": {
            "type": "integer",
            "minimum": 1
          },
          "domain": {
            "type": "string"
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "scoreTotal": {
            "type": "integer"
          },
          "grade": {
            "type": "string"
          },
          "rubricVersion": {
            "type": "string"
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time"
          },
          "reportUrl": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Leaderboard": {
        "type": "object",
        "required": [
          "category",
          "categories",
          "total",
          "offset",
          "limit",
          "rubricVersion",
          "entries"
        ],
        "properties": {
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "total": {
            "type": "integer",
            "description": "Listed sites matching the filter, before paging."
          },
          "offset": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "rubricVersion": {
            "type": "string"
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LeaderboardEntry"
            }
          }
        }
      },
      "Comparison": {
        "type": "object",
        "required": [
          "compareUrl",
          "a",
          "b",
          "checks"
        ],
        "properties": {
          "compareUrl": {
            "type": "string",
            "format": "uri"
          },
          "a": {
            "$ref": "#/components/schemas/SiteSummary"
          },
          "b": {
            "$ref": "#/components/schemas/SiteSummary"
          },
          "checks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "pillar": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "pointsPossible": {
                  "type": "number"
                },
                "a": {
                  "$ref": "#/components/schemas/CheckOutcome"
                },
                "b": {
                  "$ref": "#/components/schemas/CheckOutcome"
                }
              }
            }
          }
        }
      },
      "CheckOutcome": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "pass",
              "partial",
              "fail",
              "na"
            ]
          },
          "pointsEarned": {
            "type": "number"
          }
        }
      },
      "ScanStatus": {
        "type": "object",
        "required": [
          "id",
          "domain",
          "status",
          "error",
          "createdAt",
          "progressUrl",
          "reportUrl"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "domain": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "crawling",
              "scoring",
              "complete",
              "failed",
              "blocked"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "progressUrl": {
            "type": "string",
            "format": "uri"
          },
          "reportUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          }
        }
      },
      "Rubric": {
        "type": "object",
        "required": [
          "version",
          "totalChecks",
          "totalPoints",
          "methodologyUrl",
          "pillars",
          "grades",
          "changelog"
        ],
        "properties": {
          "version": {
            "type": "string"
          },
          "totalChecks": {
            "type": "integer"
          },
          "totalPoints": {
            "type": "integer"
          },
          "methodologyUrl": {
            "type": "string",
            "format": "uri"
          },
          "pillars": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "question": {
                  "type": "string"
                },
                "points": {
                  "type": "integer"
                },
                "checks": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "points": {
                        "type": "integer"
                      },
                      "detail": {
                        "type": "string"
                      },
                      "effort": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "grades": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "grade": {
                  "type": "string"
                },
                "range": {
                  "type": "string"
                }
              }
            }
          },
          "changelog": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "version": {
                  "type": "string"
                },
                "date": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}