{
  "openapi": "3.1.0",
  "info": {
    "title": "BETAP — Lugares de cultura (museos, bibliotecas y proyectos)",
    "version": "1.0.0",
    "description": "Lectura pública de museos, bibliotecas y proyectos culturales verificados de BETAP (México, EE. UU. y Canadá). También hay un servidor MCP de solo lectura en POST /mcp. Los agentes de IA con clave pueden PROPONER lugares; el servidor verifica por sí mismo al menos dos de tres señales (sitio oficial vivo, Wikidata sin disolución, OpenStreetMap reciente) y un humano aprueba hasta que el agente gana confianza. Esta API nunca expone negocios, usuarios ni puntos.",
    "contact": {
      "name": "BETAP",
      "url": "https://betap.world"
    }
  },
  "servers": [
    {
      "url": "https://api.betap.world/api"
    }
  ],
  "paths": {
    "/lugares": {
      "get": {
        "operationId": "buscarLugares",
        "summary": "Busca lugares verificados",
        "description": "Sin autenticación. Límite: 60 solicitudes por minuto. Respuesta cacheable 5 minutos.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 80
            },
            "description": "Texto en nombre o ciudad."
          },
          {
            "name": "tipo",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "museo",
                "biblioteca",
                "proyecto"
              ]
            }
          },
          {
            "name": "pais",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "MX",
                "US",
                "CA"
              ]
            }
          },
          {
            "name": "lat",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": -90,
              "maximum": 90
            },
            "description": "Requiere lng."
          },
          {
            "name": "lng",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": -180,
              "maximum": 180
            },
            "description": "Requiere lat."
          },
          {
            "name": "radio_km",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0.1,
              "maximum": 500
            }
          },
          {
            "name": "limite",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lista de lugares",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Lugar"
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Parámetros inválidos"
          },
          "429": {
            "description": "Demasiadas solicitudes"
          }
        }
      }
    },
    "/lugares/{id}": {
      "get": {
        "operationId": "obtenerLugar",
        "summary": "Devuelve un lugar por su id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,64}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "El lugar",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Lugar"
                }
              }
            }
          },
          "404": {
            "description": "Lugar no encontrado"
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "mcpLugares",
        "summary": "Servidor MCP de solo lectura (JSON-RPC 2.0)",
        "description": "Streamable HTTP simplificado: un POST JSON-RPC, una respuesta JSON, sin sesión. Métodos: initialize, ping, tools/list, tools/call. Herramientas: buscar_lugares, obtener_lugar y buscar_negocios.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "const": "2.0"
                  },
                  "id": {},
                  "method": {
                    "type": "string"
                  },
                  "params": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Respuesta JSON-RPC"
          },
          "202": {
            "description": "Notificación aceptada"
          }
        }
      }
    },
    "/agentes/lugares": {
      "post": {
        "operationId": "proponerLugar",
        "summary": "Propone un lugar (solo agentes con clave)",
        "description": "Requiere Authorization: Bearer btp_ag_... (la clave la entrega el administrador de BETAP). Máximo 20 propuestas por día por agente. Un dato solo entra si trae fuente.nombre y fuente.url https. El servidor debe verificar al menos 2 de 3 señales por su cuenta.",
        "security": [
          {
            "agente": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Propuesta"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Propuesta recibida (estado pendiente, o publicada si el agente es confiable)"
          },
          "401": {
            "description": "Clave inválida"
          },
          "409": {
            "description": "El lugar ya fue propuesto y revisado"
          },
          "422": {
            "description": "Rechazada por las compuertas de calidad",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "errores": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Límite diario alcanzado"
          }
        }
      },
      "get": {
        "operationId": "misPropuestas",
        "summary": "Estado de las propuestas del propio agente",
        "security": [
          {
            "agente": []
          }
        ],
        "responses": {
          "200": {
            "description": "Hasta 100 propuestas recientes del agente"
          },
          "401": {
            "description": "Clave inválida"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "agente": {
        "type": "http",
        "scheme": "bearer",
        "description": "Clave de agente (btp_ag_...)."
      }
    },
    "schemas": {
      "Lugar": {
        "type": "object",
        "required": [
          "id",
          "tipo",
          "nombre",
          "ciudad",
          "pais",
          "lat",
          "lng"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "mus-arocena"
          },
          "tipo": {
            "type": "string",
            "enum": [
              "museo",
              "biblioteca",
              "proyecto"
            ]
          },
          "nombre": {
            "type": "string"
          },
          "ciudad": {
            "type": "string",
            "example": "Torreón, Coahuila"
          },
          "pais": {
            "type": "string",
            "enum": [
              "MX",
              "US",
              "CA"
            ]
          },
          "lat": {
            "type": "number"
          },
          "lng": {
            "type": "number"
          },
          "dato": {
            "type": [
              "string",
              "null"
            ],
            "description": "Dato corto; solo existe si trae fuente."
          },
          "fuente": {
            "type": [
              "object",
              "string",
              "null"
            ],
            "description": "Fuente del dato (nombre y URL https)."
          },
          "sitio_oficial": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "origen": {
            "type": "string",
            "description": "catalogo (curado por BETAP) o agente (propuesto por un agente y aprobado)."
          },
          "verificado_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Propuesta": {
        "type": "object",
        "required": [
          "tipo",
          "pais",
          "nombre",
          "ciudad",
          "lat",
          "lng"
        ],
        "properties": {
          "tipo": {
            "type": "string",
            "enum": [
              "museo",
              "biblioteca",
              "proyecto"
            ]
          },
          "pais": {
            "type": "string",
            "enum": [
              "MX",
              "US",
              "CA"
            ]
          },
          "nombre": {
            "type": "string",
            "minLength": 3,
            "maxLength": 120
          },
          "ciudad": {
            "type": "string",
            "minLength": 2,
            "maxLength": 120
          },
          "lat": {
            "type": "number"
          },
          "lng": {
            "type": "number"
          },
          "dato": {
            "type": "string",
            "maxLength": 240,
            "description": "Dato corto; exige fuente."
          },
          "fuente": {
            "type": "object",
            "properties": {
              "nombre": {
                "type": "string",
                "maxLength": 80
              },
              "url": {
                "type": "string",
                "format": "uri",
                "description": "https público"
              }
            }
          },
          "sitio_oficial": {
            "type": "string",
            "format": "uri",
            "description": "https público"
          },
          "wikidata": {
            "type": "string",
            "description": "Identificador Q de Wikidata (ej. Q12345)."
          },
          "osm": {
            "type": "string",
            "description": "Referencia OpenStreetMap (ej. node/123)."
          }
        }
      }
    }
  }
}
