{
  "openapi": "3.1.0",
  "info": {
    "title": "Agents Allowed lookup API",
    "version": "1.2.0",
    "termsOfService": "https://agentsallowed.io/terms/",
    "contact": {
      "name": "Agents Allowed",
      "url": "https://agentsallowed.io/submit/",
      "email": "contact@agentsallowed.io"
    },
    "description": "Read-only lookup: does a website let AI agents browse, shop, book or log in, and is there an official agent route? No auth. CORS open. Informational only, no warranty: see https://agentsallowed.io/disclaimer/. recommended_action is a suggestion based on public sources, not permission; the website's own terms always take priority. Endpoints marked (worker) need the edge worker; static files work on any host. Rate limits (enforced): 60 requests per minute per IP on every /api/ path, /mcp and /a2a; 10 submissions per hour per IP. Over a limit: HTTP 429 with Retry-After, RateLimit and RateLimit-Policy headers and a JSON error {\"error\":\"rate_limited\"}."
  },
  "servers": [
    {
      "url": "https://agentsallowed.io"
    }
  ],
  "paths": {
    "/api/v1/check": {
      "get": {
        "operationId": "checkDomain",
        "summary": "Check one domain (domain=) or up to 50 (domains=a,b) (worker)",
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "example": "delta.com"
          },
          {
            "name": "domains",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "example": "delta.com,target.com"
          }
        ],
        "responses": {
          "200": {
            "description": "Record, or batch {results:[record]}",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "https://agentsallowed.io/api/v1/schema/record.schema.json"
                }
              }
            }
          },
          "304": {
            "description": "Not modified (If-None-Match / If-Modified-Since)"
          },
          "400": {
            "description": "Invalid domain"
          },
          "404": {
            "description": "Not tracked: record with found=false, status=unknown",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "https://agentsallowed.io/api/v1/schema/record.schema.json"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "checkDomains",
        "summary": "Batch check, max 50 (worker)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domains"
                ],
                "properties": {
                  "domains": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{schema_version, count, results:[record]} in input order"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/domains/{domain}.json": {
      "get": {
        "operationId": "getDomain",
        "summary": "Static record for an exact tracked domain",
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "https://agentsallowed.io/api/v1/schema/record.schema.json"
                }
              }
            }
          },
          "404": {
            "description": "Not tracked"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/index.json": {
      "get": {
        "operationId": "getIndex",
        "summary": "All domains, aliases (domain_map), statuses, enums, dataset_version",
        "responses": {
          "200": {
            "description": "Index"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/all.json": {
      "get": {
        "operationId": "getAll",
        "summary": "Full dataset (all records)",
        "responses": {
          "200": {
            "description": "Bulk",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "https://agentsallowed.io/api/v1/schema/all.schema.json"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/all.csv": {
      "get": {
        "operationId": "getAllCsv",
        "summary": "Full dataset as CSV",
        "responses": {
          "200": {
            "description": "CSV",
            "content": {
              "text/csv": {}
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/changes.json": {
      "get": {
        "operationId": "getChanges",
        "summary": "Changes feed (newest first). Sync by dataset_version / change id",
        "responses": {
          "200": {
            "description": "Changes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "https://agentsallowed.io/api/v1/schema/changes.schema.json"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/changes": {
      "get": {
        "operationId": "getChangesSince",
        "summary": "Changes, optionally since a date and/or for one status (worker)",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "partial",
                "announced",
                "none"
              ]
            },
            "description": "open | partial | announced | none"
          }
        ],
        "responses": {
          "200": {
            "description": "Changes"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/submissions": {
      "post": {
        "operationId": "submitUpdate",
        "summary": "Submit an update: add_brand, correct_listing, report_block or report_works. Goes to HUMAN REVIEW; nothing auto-publishes (worker; returns 501 until intake is enabled)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "https://agentsallowed.io/api/v1/schema/submission.schema.json"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued for human review (review_status pending_review, published false)"
          },
          "400": {
            "description": "Invalid submission (field errors)"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "501": {
            "description": "Intake not enabled yet; nothing stored"
          }
        }
      }
    },
    "/api/v1/reports": {
      "post": {
        "operationId": "submitReport",
        "deprecated": true,
        "summary": "Legacy alias of /api/v1/submissions (accepts the old report body)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "https://agentsallowed.io/api/v1/schema/report.schema.json"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued for human review"
          },
          "400": {
            "description": "Invalid"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "501": {
            "description": "Intake not enabled yet; nothing stored"
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "RateLimited": {
        "description": "Too many requests: 60 requests per minute per IP (10 submissions per hour per IP for submissions). Wait for Retry-After seconds.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying",
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit": {
            "description": "Remaining quota and reset, e.g. \"default\";r=0;t=60",
            "schema": {
              "type": "string"
            }
          },
          "RateLimit-Policy": {
            "description": "\"default\";q=60;w=60 or \"submissions\";q=10;w=3600",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "error",
                "scope",
                "limit",
                "window_seconds",
                "retry_after_seconds",
                "message"
              ],
              "properties": {
                "error": {
                  "const": "rate_limited"
                },
                "scope": {
                  "enum": [
                    "default",
                    "submissions"
                  ]
                },
                "limit": {
                  "type": "integer"
                },
                "window_seconds": {
                  "type": "integer"
                },
                "retry_after_seconds": {
                  "type": "integer"
                },
                "message": {
                  "type": "string"
                },
                "docs": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}
