{
  "openapi": "3.1.0",
  "info": {
    "title": "API de datos de Luthor (REST v1)",
    "version": "luthor-datos/1.3",
    "summary": "Tesis y leyes de Luthor: buscar y leer tesis, artículos y la normativa del archivo.",
    "description": "Contrato `luthor-datos/1`. La fuente que se atribuye es «Archivo Soberano Luthor». Documentación completa: https://mcp.luthor.mx/docs. La información es de consulta y no constituye asesoría jurídica.",
    "contact": {
      "name": "Soporte",
      "email": "jccc@bytx.dev"
    }
  },
  "servers": [
    {
      "url": "https://mcp.luthor.mx",
      "description": "Producción: llaves `lxk_live_`."
    },
    {
      "url": "https://sandbox.luthor.mx",
      "description": "Sandbox: llaves `lxk_test_`, corpus fijo y sin cargo."
    }
  ],
  "security": [
    {
      "llave": []
    }
  ],
  "tags": [
    {
      "name": "Tesis"
    },
    {
      "name": "Artículos"
    },
    {
      "name": "Ordenamientos"
    }
  ],
  "paths": {
    "/v1/tesis/buscar": {
      "post": {
        "operationId": "luthor_buscar_tesis",
        "summary": "Buscar tesis",
        "description": "Busca por significado y por palabras tesis del Archivo Soberano Luthor: jurisprudencias y tesis aisladas del Poder Judicial de la Federación y, según el plan, del Tribunal Federal de Justicia Administrativa. Úsala cuando necesites criterios judiciales sobre un tema o un problema jurídico y aún no tengas el identificador de la tesis. Devuelve hasta 10 resultados con extracto, posición, puntaje, procedencia, estado y situación de la tesis, la fecha de corte del archivo y, en las del Tribunal Federal de Justicia Administrativa, a quién obligan. Tool MCP gemela: `luthor_buscar_tesis` (mismo JSON).",
        "tags": [
          "Tesis"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Opcional, de 1 a 255 caracteres ASCII visibles. Misma clave y mismo cuerpo dentro de 24 h devuelven el mismo evento sin segundo cobro; otra solicitud con la misma clave es 409.",
            "schema": {
              "type": "string",
              "pattern": "^[\\x21-\\x7E]{1,255}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/luthor_buscar_tesis.entrada"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Éxito: el cuerpo ES el `structuredContent` de la tool MCP gemela.",
            "headers": {
              "Luthor-Unidades": {
                "description": "Unidades de cuota que cobró esta llamada.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Luthor-Evento": {
                "description": "Identificador del evento de cobro, generado por el servidor.",
                "schema": {
                  "type": "string"
                }
              },
              "Luthor-Repeticion": {
                "description": "`1` si es una repetición idempotente (sin segundo cobro).",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "Luthor-Contrato": {
                "description": "Versión del contrato.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "luthor-datos/1"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/luthor_buscar_tesis.salida"
                }
              }
            }
          },
          "400": {
            "description": "`SOLICITUD_MALFORMADA`: Envía un cuerpo JSON bien formado. El cuerpo no es JSON o no es UTF-8 legible (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "401": {
            "description": "`LLAVE_AUSENTE`: Envía `Authorization: Bearer <llave>`. `LLAVE_INVALIDA`: Usa una llave vigente de tu organización.",
            "headers": {
              "WWW-Authenticate": {
                "description": "`Bearer realm=\"luthor-datos\"`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "402": {
            "description": "`CUOTA_AGOTADA`: El administrador de la organización puede permitir excedente en el portal; la cuota reinicia el primer día del mes (hora de CDMX). `TOPE_ALCANZADO`: El administrador de la organización puede subir el tope en el portal; reinicia el primer día del mes (hora de CDMX). `COBERTURA_AGOTADA`: Para volúmenes mayores, el plan Datos incluye entregas en bloque.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "403": {
            "description": "`ORG_SUSPENDIDA`: El administrador de la organización debe contactar a soporte. `ENTORNO_CRUZADO`: Usa una llave `test` en el sandbox y una `live` en producción. `SCOPE_INSUFICIENTE`: El administrador de la organización puede crear una llave con el permiso necesario.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "404": {
            "description": "`NO_ENCONTRADO`: Busca primero y usa un identificador de los resultados. `NO_MIGRADO`: Busca en el corpus curado un documento equivalente. `SIN_TEXTO_LEGIBLE`: No reconstruyas el texto ni lo cites de memoria: nombra la norma por su título y su URL, donde está el facsímil si existe. `HERRAMIENTA_DESCONOCIDA`: Usa una de las cuatro operaciones publicadas.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "405": {
            "description": "Método incorrecto para la ruta (`SOLICITUD_MALFORMADA`, con `Allow`).",
            "headers": {
              "Allow": {
                "description": "El método que acepta la ruta.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "409": {
            "description": "`IDEMPOTENCIA_EN_CONFLICTO`: Usa una `Idempotency-Key` nueva para cada solicitud distinta. `IDEMPOTENCIA_EN_CURSO`: Espera los segundos indicados y repite la misma solicitud.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "410": {
            "description": "`DEROGADO`: Busca el artículo vigente que regula la materia.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "413": {
            "description": "El cuerpo excede 64 KiB (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "422": {
            "description": "`ARGUMENTO_INVALIDO`: Corrige el argumento indicado en `campo` y vuelve a llamar. `ID_INVALIDO`: Usa un identificador devuelto por la búsqueda de la misma familia. `LEY_FUERA_DE_CATALOGO`: Usa uno de los códigos de ley del catálogo (p. ej. CFF, LISR, LIVA). `FRAGMENTO_FUERA_DE_RANGO`: Pide un fragmento entre 1 y `fragmentos_totales`. `TECHO_INSUFICIENTE`: Pide un techo de salida mayor, hasta el máximo contractual.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "429": {
            "description": "`LIMITE_DIARIO`: El límite se renueva a las 00:00 (hora de CDMX); reintenta después de los segundos indicados. `RAFAGA_EXCEDIDA`: Espera los segundos indicados y reintenta.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "500": {
            "description": "`ERROR_INTERNO`: Reintenta; si persiste, escribe a soporte. No se cobró la llamada.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "503": {
            "description": "`TEMPORALMENTE_NO_DISPONIBLE`: Reintenta en unos segundos; no se cobró la llamada.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          }
        }
      }
    },
    "/v1/tesis/{id}": {
      "get": {
        "operationId": "luthor_leer_tesis",
        "summary": "Leer tesis",
        "description": "Lee una tesis del Archivo Soberano Luthor por su identificador (JP-… del Poder Judicial de la Federación o, según el plan, TFJA-… del Tribunal Federal de Justicia Administrativa). Úsala cuando ya tengas el identificador de la tesis (p. ej. de un resultado de luthor_buscar_tesis) y necesites su texto para citarla o analizarla. Devuelve el texto por fragmentos fijos, con procedencia, estado y situación de la tesis, el truncado declarado y la fecha de corte del archivo. Tool MCP gemela: `luthor_leer_tesis` (mismo JSON).",
        "tags": [
          "Tesis"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador de la tesis, p. ej. JP-159807 (Poder Judicial de la Federación) o TFJA-48288 (Tribunal Federal de Justicia Administrativa).",
            "schema": {
              "type": "string",
              "pattern": "^(?:JP-[1-9][0-9]{0,11}|TFJA-[1-9][0-9]{0,8})$",
              "description": "Identificador de la tesis, p. ej. JP-159807 (Poder Judicial de la Federación) o TFJA-48288 (Tribunal Federal de Justicia Administrativa)."
            }
          },
          {
            "name": "fragmento",
            "in": "query",
            "required": false,
            "description": "Fragmento del documento, base 1. Los fragmentos son fijos por documento; cada uno es una lectura.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100000,
              "description": "Fragmento del documento, base 1. Los fragmentos son fijos por documento; cada uno es una lectura.",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Éxito: el cuerpo ES el `structuredContent` de la tool MCP gemela.",
            "headers": {
              "Luthor-Unidades": {
                "description": "Unidades de cuota que cobró esta llamada.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Luthor-Evento": {
                "description": "Identificador del evento de cobro, generado por el servidor.",
                "schema": {
                  "type": "string"
                }
              },
              "Luthor-Repeticion": {
                "description": "`1` si es una repetición idempotente (sin segundo cobro).",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "Luthor-Contrato": {
                "description": "Versión del contrato.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "luthor-datos/1"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/luthor_leer_tesis.salida"
                }
              }
            }
          },
          "400": {
            "description": "`SOLICITUD_MALFORMADA`: Envía un cuerpo JSON bien formado. El cuerpo no es JSON o no es UTF-8 legible (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "401": {
            "description": "`LLAVE_AUSENTE`: Envía `Authorization: Bearer <llave>`. `LLAVE_INVALIDA`: Usa una llave vigente de tu organización.",
            "headers": {
              "WWW-Authenticate": {
                "description": "`Bearer realm=\"luthor-datos\"`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "402": {
            "description": "`CUOTA_AGOTADA`: El administrador de la organización puede permitir excedente en el portal; la cuota reinicia el primer día del mes (hora de CDMX). `TOPE_ALCANZADO`: El administrador de la organización puede subir el tope en el portal; reinicia el primer día del mes (hora de CDMX). `COBERTURA_AGOTADA`: Para volúmenes mayores, el plan Datos incluye entregas en bloque.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "403": {
            "description": "`ORG_SUSPENDIDA`: El administrador de la organización debe contactar a soporte. `ENTORNO_CRUZADO`: Usa una llave `test` en el sandbox y una `live` en producción. `SCOPE_INSUFICIENTE`: El administrador de la organización puede crear una llave con el permiso necesario.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "404": {
            "description": "`NO_ENCONTRADO`: Busca primero y usa un identificador de los resultados. `NO_MIGRADO`: Busca en el corpus curado un documento equivalente. `SIN_TEXTO_LEGIBLE`: No reconstruyas el texto ni lo cites de memoria: nombra la norma por su título y su URL, donde está el facsímil si existe. `HERRAMIENTA_DESCONOCIDA`: Usa una de las cuatro operaciones publicadas.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "405": {
            "description": "Método incorrecto para la ruta (`SOLICITUD_MALFORMADA`, con `Allow`).",
            "headers": {
              "Allow": {
                "description": "El método que acepta la ruta.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "409": {
            "description": "`IDEMPOTENCIA_EN_CONFLICTO`: Usa una `Idempotency-Key` nueva para cada solicitud distinta. `IDEMPOTENCIA_EN_CURSO`: Espera los segundos indicados y repite la misma solicitud.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "410": {
            "description": "`DEROGADO`: Busca el artículo vigente que regula la materia.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "413": {
            "description": "El cuerpo excede 64 KiB (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "422": {
            "description": "`ARGUMENTO_INVALIDO`: Corrige el argumento indicado en `campo` y vuelve a llamar. `ID_INVALIDO`: Usa un identificador devuelto por la búsqueda de la misma familia. `LEY_FUERA_DE_CATALOGO`: Usa uno de los códigos de ley del catálogo (p. ej. CFF, LISR, LIVA). `FRAGMENTO_FUERA_DE_RANGO`: Pide un fragmento entre 1 y `fragmentos_totales`. `TECHO_INSUFICIENTE`: Pide un techo de salida mayor, hasta el máximo contractual.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "429": {
            "description": "`LIMITE_DIARIO`: El límite se renueva a las 00:00 (hora de CDMX); reintenta después de los segundos indicados. `RAFAGA_EXCEDIDA`: Espera los segundos indicados y reintenta.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "500": {
            "description": "`ERROR_INTERNO`: Reintenta; si persiste, escribe a soporte. No se cobró la llamada.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "503": {
            "description": "`TEMPORALMENTE_NO_DISPONIBLE`: Reintenta en unos segundos; no se cobró la llamada.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          }
        }
      }
    },
    "/v1/articulos/buscar": {
      "post": {
        "operationId": "luthor_buscar_articulo",
        "summary": "Buscar artículos",
        "description": "Busca por coincidencia léxica artículos de las 16 leyes de Luthor. Úsala cuando necesites saber qué artículos regulan un tema y no conozcas su número, en todas las leyes o en una sola. Devuelve hasta 10 resultados con extracto, URL, posición, puntaje, procedencia y vigencia. Tool MCP gemela: `luthor_buscar_articulo` (mismo JSON).",
        "tags": [
          "Artículos"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Opcional, de 1 a 255 caracteres ASCII visibles. Misma clave y mismo cuerpo dentro de 24 h devuelven el mismo evento sin segundo cobro; otra solicitud con la misma clave es 409.",
            "schema": {
              "type": "string",
              "pattern": "^[\\x21-\\x7E]{1,255}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/luthor_buscar_articulo.entrada"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Éxito: el cuerpo ES el `structuredContent` de la tool MCP gemela.",
            "headers": {
              "Luthor-Unidades": {
                "description": "Unidades de cuota que cobró esta llamada.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Luthor-Evento": {
                "description": "Identificador del evento de cobro, generado por el servidor.",
                "schema": {
                  "type": "string"
                }
              },
              "Luthor-Repeticion": {
                "description": "`1` si es una repetición idempotente (sin segundo cobro).",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "Luthor-Contrato": {
                "description": "Versión del contrato.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "luthor-datos/1"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/luthor_buscar_articulo.salida"
                }
              }
            }
          },
          "400": {
            "description": "`SOLICITUD_MALFORMADA`: Envía un cuerpo JSON bien formado. El cuerpo no es JSON o no es UTF-8 legible (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "401": {
            "description": "`LLAVE_AUSENTE`: Envía `Authorization: Bearer <llave>`. `LLAVE_INVALIDA`: Usa una llave vigente de tu organización.",
            "headers": {
              "WWW-Authenticate": {
                "description": "`Bearer realm=\"luthor-datos\"`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "402": {
            "description": "`CUOTA_AGOTADA`: El administrador de la organización puede permitir excedente en el portal; la cuota reinicia el primer día del mes (hora de CDMX). `TOPE_ALCANZADO`: El administrador de la organización puede subir el tope en el portal; reinicia el primer día del mes (hora de CDMX). `COBERTURA_AGOTADA`: Para volúmenes mayores, el plan Datos incluye entregas en bloque.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "403": {
            "description": "`ORG_SUSPENDIDA`: El administrador de la organización debe contactar a soporte. `ENTORNO_CRUZADO`: Usa una llave `test` en el sandbox y una `live` en producción. `SCOPE_INSUFICIENTE`: El administrador de la organización puede crear una llave con el permiso necesario.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "404": {
            "description": "`NO_ENCONTRADO`: Busca primero y usa un identificador de los resultados. `NO_MIGRADO`: Busca en el corpus curado un documento equivalente. `SIN_TEXTO_LEGIBLE`: No reconstruyas el texto ni lo cites de memoria: nombra la norma por su título y su URL, donde está el facsímil si existe. `HERRAMIENTA_DESCONOCIDA`: Usa una de las cuatro operaciones publicadas.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "405": {
            "description": "Método incorrecto para la ruta (`SOLICITUD_MALFORMADA`, con `Allow`).",
            "headers": {
              "Allow": {
                "description": "El método que acepta la ruta.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "409": {
            "description": "`IDEMPOTENCIA_EN_CONFLICTO`: Usa una `Idempotency-Key` nueva para cada solicitud distinta. `IDEMPOTENCIA_EN_CURSO`: Espera los segundos indicados y repite la misma solicitud.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "410": {
            "description": "`DEROGADO`: Busca el artículo vigente que regula la materia.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "413": {
            "description": "El cuerpo excede 64 KiB (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "422": {
            "description": "`ARGUMENTO_INVALIDO`: Corrige el argumento indicado en `campo` y vuelve a llamar. `ID_INVALIDO`: Usa un identificador devuelto por la búsqueda de la misma familia. `LEY_FUERA_DE_CATALOGO`: Usa uno de los códigos de ley del catálogo (p. ej. CFF, LISR, LIVA). `FRAGMENTO_FUERA_DE_RANGO`: Pide un fragmento entre 1 y `fragmentos_totales`. `TECHO_INSUFICIENTE`: Pide un techo de salida mayor, hasta el máximo contractual.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "429": {
            "description": "`LIMITE_DIARIO`: El límite se renueva a las 00:00 (hora de CDMX); reintenta después de los segundos indicados. `RAFAGA_EXCEDIDA`: Espera los segundos indicados y reintenta.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "500": {
            "description": "`ERROR_INTERNO`: Reintenta; si persiste, escribe a soporte. No se cobró la llamada.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "503": {
            "description": "`TEMPORALMENTE_NO_DISPONIBLE`: Reintenta en unos segundos; no se cobró la llamada.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          }
        }
      }
    },
    "/v1/articulos/{id}": {
      "get": {
        "operationId": "luthor_leer_articulo",
        "summary": "Leer artículo",
        "description": "Lee un artículo de las 16 leyes de Luthor, por identificador o por ley y número. Úsala cuando necesites el texto de un artículo o de una regla que ya identificaste, p. ej. para citarlo o revisar su vigencia. Devuelve el texto por fragmentos fijos, con procedencia, vigencia y el truncado declarado. Tool MCP gemela: `luthor_leer_articulo` (mismo JSON).",
        "tags": [
          "Artículos"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador del artículo, p. ej. CFF-art-17-H-Bis o RMF-2026-cap-2.1.1.",
            "schema": {
              "type": "string",
              "maxLength": 64,
              "pattern": "^(?:[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*-(?:art|cap)-[A-Za-z0-9.]+(?:-[A-Za-z0-9]+)*|[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*-transitorios|RMF-[0-9]{4}-00-glosario|RMF-[0-9]{4}-01-tit-[0-9]+|RMF-[0-9]{4}-anexo-[0-9]+|decreto(?:-[a-z0-9]+)+)$",
              "not": {
                "pattern": "\\.\\."
              },
              "description": "Identificador del artículo, p. ej. CFF-art-17-H-Bis o RMF-2026-cap-2.1.1."
            }
          },
          {
            "name": "fragmento",
            "in": "query",
            "required": false,
            "description": "Fragmento del documento, base 1. Los fragmentos son fijos por documento; cada uno es una lectura.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100000,
              "description": "Fragmento del documento, base 1. Los fragmentos son fijos por documento; cada uno es una lectura.",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Éxito: el cuerpo ES el `structuredContent` de la tool MCP gemela.",
            "headers": {
              "Luthor-Unidades": {
                "description": "Unidades de cuota que cobró esta llamada.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Luthor-Evento": {
                "description": "Identificador del evento de cobro, generado por el servidor.",
                "schema": {
                  "type": "string"
                }
              },
              "Luthor-Repeticion": {
                "description": "`1` si es una repetición idempotente (sin segundo cobro).",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "Luthor-Contrato": {
                "description": "Versión del contrato.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "luthor-datos/1"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/luthor_leer_articulo.salida"
                }
              }
            }
          },
          "400": {
            "description": "`SOLICITUD_MALFORMADA`: Envía un cuerpo JSON bien formado. El cuerpo no es JSON o no es UTF-8 legible (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "401": {
            "description": "`LLAVE_AUSENTE`: Envía `Authorization: Bearer <llave>`. `LLAVE_INVALIDA`: Usa una llave vigente de tu organización.",
            "headers": {
              "WWW-Authenticate": {
                "description": "`Bearer realm=\"luthor-datos\"`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "402": {
            "description": "`CUOTA_AGOTADA`: El administrador de la organización puede permitir excedente en el portal; la cuota reinicia el primer día del mes (hora de CDMX). `TOPE_ALCANZADO`: El administrador de la organización puede subir el tope en el portal; reinicia el primer día del mes (hora de CDMX). `COBERTURA_AGOTADA`: Para volúmenes mayores, el plan Datos incluye entregas en bloque.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "403": {
            "description": "`ORG_SUSPENDIDA`: El administrador de la organización debe contactar a soporte. `ENTORNO_CRUZADO`: Usa una llave `test` en el sandbox y una `live` en producción. `SCOPE_INSUFICIENTE`: El administrador de la organización puede crear una llave con el permiso necesario.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "404": {
            "description": "`NO_ENCONTRADO`: Busca primero y usa un identificador de los resultados. `NO_MIGRADO`: Busca en el corpus curado un documento equivalente. `SIN_TEXTO_LEGIBLE`: No reconstruyas el texto ni lo cites de memoria: nombra la norma por su título y su URL, donde está el facsímil si existe. `HERRAMIENTA_DESCONOCIDA`: Usa una de las cuatro operaciones publicadas.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "405": {
            "description": "Método incorrecto para la ruta (`SOLICITUD_MALFORMADA`, con `Allow`).",
            "headers": {
              "Allow": {
                "description": "El método que acepta la ruta.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "409": {
            "description": "`IDEMPOTENCIA_EN_CONFLICTO`: Usa una `Idempotency-Key` nueva para cada solicitud distinta. `IDEMPOTENCIA_EN_CURSO`: Espera los segundos indicados y repite la misma solicitud.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "410": {
            "description": "`DEROGADO`: Busca el artículo vigente que regula la materia.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "413": {
            "description": "El cuerpo excede 64 KiB (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "422": {
            "description": "`ARGUMENTO_INVALIDO`: Corrige el argumento indicado en `campo` y vuelve a llamar. `ID_INVALIDO`: Usa un identificador devuelto por la búsqueda de la misma familia. `LEY_FUERA_DE_CATALOGO`: Usa uno de los códigos de ley del catálogo (p. ej. CFF, LISR, LIVA). `FRAGMENTO_FUERA_DE_RANGO`: Pide un fragmento entre 1 y `fragmentos_totales`. `TECHO_INSUFICIENTE`: Pide un techo de salida mayor, hasta el máximo contractual.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "429": {
            "description": "`LIMITE_DIARIO`: El límite se renueva a las 00:00 (hora de CDMX); reintenta después de los segundos indicados. `RAFAGA_EXCEDIDA`: Espera los segundos indicados y reintenta.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "500": {
            "description": "`ERROR_INTERNO`: Reintenta; si persiste, escribe a soporte. No se cobró la llamada.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "503": {
            "description": "`TEMPORALMENTE_NO_DISPONIBLE`: Reintenta en unos segundos; no se cobró la llamada.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          }
        }
      }
    },
    "/v1/ordenamientos/buscar": {
      "post": {
        "operationId": "luthor_buscar_ordenamiento",
        "summary": "Buscar ordenamientos",
        "description": "Busca por palabras normas del Archivo Soberano Luthor: leyes, códigos, reglamentos, decretos y tratados de todo México (federales, de las 32 entidades federativas e internacionales). Úsala cuando necesites ubicar una norma que no está en las 16 leyes de Luthor, p. ej. una ley estatal, un código civil local o un tratado, y aún no tengas su identificador. Devuelve hasta 10 normas con su identificador ORD-, título, ámbito, entidad y categoría, la vigencia declarada con su fuente y su fecha de corte, y la versión vigente con el tipo de texto que tiene. Tool MCP gemela: `luthor_buscar_ordenamiento` (mismo JSON).",
        "tags": [
          "Ordenamientos"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Opcional, de 1 a 255 caracteres ASCII visibles. Misma clave y mismo cuerpo dentro de 24 h devuelven el mismo evento sin segundo cobro; otra solicitud con la misma clave es 409.",
            "schema": {
              "type": "string",
              "pattern": "^[\\x21-\\x7E]{1,255}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/luthor_buscar_ordenamiento.entrada"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Éxito: el cuerpo ES el `structuredContent` de la tool MCP gemela.",
            "headers": {
              "Luthor-Unidades": {
                "description": "Unidades de cuota que cobró esta llamada.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Luthor-Evento": {
                "description": "Identificador del evento de cobro, generado por el servidor.",
                "schema": {
                  "type": "string"
                }
              },
              "Luthor-Repeticion": {
                "description": "`1` si es una repetición idempotente (sin segundo cobro).",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "Luthor-Contrato": {
                "description": "Versión del contrato.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "luthor-datos/1"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/luthor_buscar_ordenamiento.salida"
                }
              }
            }
          },
          "400": {
            "description": "`SOLICITUD_MALFORMADA`: Envía un cuerpo JSON bien formado. El cuerpo no es JSON o no es UTF-8 legible (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "401": {
            "description": "`LLAVE_AUSENTE`: Envía `Authorization: Bearer <llave>`. `LLAVE_INVALIDA`: Usa una llave vigente de tu organización.",
            "headers": {
              "WWW-Authenticate": {
                "description": "`Bearer realm=\"luthor-datos\"`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "402": {
            "description": "`CUOTA_AGOTADA`: El administrador de la organización puede permitir excedente en el portal; la cuota reinicia el primer día del mes (hora de CDMX). `TOPE_ALCANZADO`: El administrador de la organización puede subir el tope en el portal; reinicia el primer día del mes (hora de CDMX). `COBERTURA_AGOTADA`: Para volúmenes mayores, el plan Datos incluye entregas en bloque.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "403": {
            "description": "`ORG_SUSPENDIDA`: El administrador de la organización debe contactar a soporte. `ENTORNO_CRUZADO`: Usa una llave `test` en el sandbox y una `live` en producción. `SCOPE_INSUFICIENTE`: El administrador de la organización puede crear una llave con el permiso necesario.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "404": {
            "description": "`NO_ENCONTRADO`: Busca primero y usa un identificador de los resultados. `NO_MIGRADO`: Busca en el corpus curado un documento equivalente. `SIN_TEXTO_LEGIBLE`: No reconstruyas el texto ni lo cites de memoria: nombra la norma por su título y su URL, donde está el facsímil si existe. `HERRAMIENTA_DESCONOCIDA`: Usa una de las cuatro operaciones publicadas.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "405": {
            "description": "Método incorrecto para la ruta (`SOLICITUD_MALFORMADA`, con `Allow`).",
            "headers": {
              "Allow": {
                "description": "El método que acepta la ruta.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "409": {
            "description": "`IDEMPOTENCIA_EN_CONFLICTO`: Usa una `Idempotency-Key` nueva para cada solicitud distinta. `IDEMPOTENCIA_EN_CURSO`: Espera los segundos indicados y repite la misma solicitud.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "410": {
            "description": "`DEROGADO`: Busca el artículo vigente que regula la materia.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "413": {
            "description": "El cuerpo excede 64 KiB (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "422": {
            "description": "`ARGUMENTO_INVALIDO`: Corrige el argumento indicado en `campo` y vuelve a llamar. `ID_INVALIDO`: Usa un identificador devuelto por la búsqueda de la misma familia. `LEY_FUERA_DE_CATALOGO`: Usa uno de los códigos de ley del catálogo (p. ej. CFF, LISR, LIVA). `FRAGMENTO_FUERA_DE_RANGO`: Pide un fragmento entre 1 y `fragmentos_totales`. `TECHO_INSUFICIENTE`: Pide un techo de salida mayor, hasta el máximo contractual.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "429": {
            "description": "`LIMITE_DIARIO`: El límite se renueva a las 00:00 (hora de CDMX); reintenta después de los segundos indicados. `RAFAGA_EXCEDIDA`: Espera los segundos indicados y reintenta.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "500": {
            "description": "`ERROR_INTERNO`: Reintenta; si persiste, escribe a soporte. No se cobró la llamada.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "503": {
            "description": "`TEMPORALMENTE_NO_DISPONIBLE`: Reintenta en unos segundos; no se cobró la llamada.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          }
        }
      }
    },
    "/v1/ordenamientos/{id}": {
      "get": {
        "operationId": "luthor_leer_ordenamiento",
        "summary": "Leer ordenamiento",
        "description": "Lee el texto de una norma del Archivo Soberano Luthor por su identificador (ORD-… de luthor_buscar_ordenamiento): todo su articulado por fragmentos fijos, un artículo por su número o los artículos que tratan un tema. Úsala cuando ya tengas el identificador de la norma y necesites su texto para citarla o analizarla. Devuelve el texto de la versión vigente, o de la versión pedida, como transcripción sin cotejar de la versión publicada, con su procedencia, la vigencia declarada y el truncado declarado; si esa versión sólo existe como facsímil, responde SIN_TEXTO_LEGIBLE y no hay texto que citar. Tool MCP gemela: `luthor_leer_ordenamiento` (mismo JSON).",
        "tags": [
          "Ordenamientos"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Identificador de la norma, p. ej. ORD-160795 (su versión vigente), o de una versión, p. ej. ORD-160795@3, tal como lo devuelve luthor_buscar_ordenamiento.",
            "schema": {
              "type": "string",
              "pattern": "^ORD-(?:0|[1-9][0-9]{0,11})(?:@(?:0|[1-9][0-9]{0,11}))?$",
              "description": "Identificador de la norma, p. ej. ORD-160795 (su versión vigente), o de una versión, p. ej. ORD-160795@3, tal como lo devuelve luthor_buscar_ordenamiento."
            }
          },
          {
            "name": "fragmento",
            "in": "query",
            "required": false,
            "description": "Fragmento del documento, base 1. Los fragmentos son fijos por documento; cada uno es una lectura.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100000,
              "description": "Fragmento del documento, base 1. Los fragmentos son fijos por documento; cada uno es una lectura.",
              "default": 1
            }
          },
          {
            "name": "articulo",
            "in": "query",
            "required": false,
            "description": "Número de un artículo de la norma, p. ej. 5, 5-A o PRIMERO. Va sin `consulta`.",
            "schema": {
              "type": "string",
              "description": "Número de un artículo de la norma, p. ej. 5, 5-A o PRIMERO. Va sin `consulta`.",
              "minLength": 1,
              "maxLength": 40,
              "pattern": "^[0-9A-Za-zÁÉÍÓÚÑáéíóúñ][0-9A-Za-zÁÉÍÓÚÑáéíóúñ .-]*$"
            }
          },
          {
            "name": "consulta",
            "in": "query",
            "required": false,
            "description": "Tema a localizar dentro de la norma: entrega hasta 5 artículos que hablan de él, en orden de relevancia. Va sin `articulo`.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "\\S",
              "description": "Tema a localizar dentro de la norma: entrega hasta 5 artículos que hablan de él, en orden de relevancia. Va sin `articulo`."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Éxito: el cuerpo ES el `structuredContent` de la tool MCP gemela.",
            "headers": {
              "Luthor-Unidades": {
                "description": "Unidades de cuota que cobró esta llamada.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Luthor-Evento": {
                "description": "Identificador del evento de cobro, generado por el servidor.",
                "schema": {
                  "type": "string"
                }
              },
              "Luthor-Repeticion": {
                "description": "`1` si es una repetición idempotente (sin segundo cobro).",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "Luthor-Contrato": {
                "description": "Versión del contrato.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "luthor-datos/1"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/luthor_leer_ordenamiento.salida"
                }
              }
            }
          },
          "400": {
            "description": "`SOLICITUD_MALFORMADA`: Envía un cuerpo JSON bien formado. El cuerpo no es JSON o no es UTF-8 legible (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "401": {
            "description": "`LLAVE_AUSENTE`: Envía `Authorization: Bearer <llave>`. `LLAVE_INVALIDA`: Usa una llave vigente de tu organización.",
            "headers": {
              "WWW-Authenticate": {
                "description": "`Bearer realm=\"luthor-datos\"`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "402": {
            "description": "`CUOTA_AGOTADA`: El administrador de la organización puede permitir excedente en el portal; la cuota reinicia el primer día del mes (hora de CDMX). `TOPE_ALCANZADO`: El administrador de la organización puede subir el tope en el portal; reinicia el primer día del mes (hora de CDMX). `COBERTURA_AGOTADA`: Para volúmenes mayores, el plan Datos incluye entregas en bloque.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "403": {
            "description": "`ORG_SUSPENDIDA`: El administrador de la organización debe contactar a soporte. `ENTORNO_CRUZADO`: Usa una llave `test` en el sandbox y una `live` en producción. `SCOPE_INSUFICIENTE`: El administrador de la organización puede crear una llave con el permiso necesario.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "404": {
            "description": "`NO_ENCONTRADO`: Busca primero y usa un identificador de los resultados. `NO_MIGRADO`: Busca en el corpus curado un documento equivalente. `SIN_TEXTO_LEGIBLE`: No reconstruyas el texto ni lo cites de memoria: nombra la norma por su título y su URL, donde está el facsímil si existe. `HERRAMIENTA_DESCONOCIDA`: Usa una de las cuatro operaciones publicadas.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "405": {
            "description": "Método incorrecto para la ruta (`SOLICITUD_MALFORMADA`, con `Allow`).",
            "headers": {
              "Allow": {
                "description": "El método que acepta la ruta.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "409": {
            "description": "`IDEMPOTENCIA_EN_CONFLICTO`: Usa una `Idempotency-Key` nueva para cada solicitud distinta. `IDEMPOTENCIA_EN_CURSO`: Espera los segundos indicados y repite la misma solicitud.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "410": {
            "description": "`DEROGADO`: Busca el artículo vigente que regula la materia.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "413": {
            "description": "El cuerpo excede 64 KiB (`SOLICITUD_MALFORMADA`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "422": {
            "description": "`ARGUMENTO_INVALIDO`: Corrige el argumento indicado en `campo` y vuelve a llamar. `ID_INVALIDO`: Usa un identificador devuelto por la búsqueda de la misma familia. `LEY_FUERA_DE_CATALOGO`: Usa uno de los códigos de ley del catálogo (p. ej. CFF, LISR, LIVA). `FRAGMENTO_FUERA_DE_RANGO`: Pide un fragmento entre 1 y `fragmentos_totales`. `TECHO_INSUFICIENTE`: Pide un techo de salida mayor, hasta el máximo contractual.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "429": {
            "description": "`LIMITE_DIARIO`: El límite se renueva a las 00:00 (hora de CDMX); reintenta después de los segundos indicados. `RAFAGA_EXCEDIDA`: Espera los segundos indicados y reintenta.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "500": {
            "description": "`ERROR_INTERNO`: Reintenta; si persiste, escribe a soporte. No se cobró la llamada.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          },
          "503": {
            "description": "`TEMPORALMENTE_NO_DISPONIBLE`: Reintenta en unos segundos; no se cobró la llamada.",
            "headers": {
              "Retry-After": {
                "description": "Segundos antes de reintentar (429, 503 e `IDEMPOTENCIA_EN_CURSO`).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problema"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "llave": {
        "type": "http",
        "scheme": "bearer",
        "description": "Llave de la organización: `Authorization: Bearer lxk_live_…` (o `lxk_test_…` en el sandbox)."
      }
    },
    "schemas": {
      "Problema": {
        "type": "object",
        "description": "Error de la REST (RFC 9457): `type`, `title`, `status` y `detail`, más el error del contrato con su `codigo` y su `accion`.",
        "properties": {
          "type": {
            "type": "string",
            "description": "URN del código de error."
          },
          "title": {
            "type": "string",
            "description": "El `mensaje` del error."
          },
          "status": {
            "type": "integer",
            "description": "Estado HTTP."
          },
          "detail": {
            "type": "string",
            "description": "La `accion` del error."
          },
          "codigo": {
            "type": "string",
            "enum": [
              "ARGUMENTO_INVALIDO",
              "ID_INVALIDO",
              "LEY_FUERA_DE_CATALOGO",
              "FRAGMENTO_FUERA_DE_RANGO",
              "TECHO_INSUFICIENTE",
              "NO_ENCONTRADO",
              "DEROGADO",
              "NO_MIGRADO",
              "SIN_TEXTO_LEGIBLE",
              "CUOTA_AGOTADA",
              "TOPE_ALCANZADO",
              "COBERTURA_AGOTADA",
              "LIMITE_DIARIO",
              "ORG_SUSPENDIDA",
              "LLAVE_AUSENTE",
              "LLAVE_INVALIDA",
              "ENTORNO_CRUZADO",
              "SCOPE_INSUFICIENTE",
              "RAFAGA_EXCEDIDA",
              "IDEMPOTENCIA_EN_CONFLICTO",
              "IDEMPOTENCIA_EN_CURSO",
              "TEMPORALMENTE_NO_DISPONIBLE",
              "ERROR_INTERNO",
              "HERRAMIENTA_DESCONOCIDA",
              "SOLICITUD_MALFORMADA"
            ]
          },
          "mensaje": {
            "type": "string",
            "minLength": 1,
            "maxLength": 400
          },
          "accion": {
            "type": "string",
            "minLength": 1,
            "maxLength": 400
          },
          "campo": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 120
              },
              {
                "type": "null"
              }
            ]
          },
          "fragmentos_totales": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 1
              },
              {
                "type": "null"
              }
            ]
          },
          "reintentar_en_s": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "codigo",
          "mensaje",
          "accion",
          "campo",
          "fragmentos_totales",
          "reintentar_en_s"
        ],
        "additionalProperties": false
      },
      "luthor_buscar_tesis.entrada": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_buscar_tesis.entrada",
        "title": "Buscar tesis: entrada",
        "type": "object",
        "properties": {
          "consulta": {
            "type": "string",
            "description": "De 1 a 4,000 caracteres; en modo \"completa\", hasta 1,000.",
            "minLength": 1,
            "maxLength": 4000,
            "pattern": "\\S"
          },
          "limite": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10,
            "description": "Cuántos resultados devolver.",
            "default": 5
          },
          "modo": {
            "type": "string",
            "enum": [
              "rapida",
              "completa"
            ],
            "description": "«rapida»: búsqueda semántica y léxica sin modelo de lenguaje, en las tesis del Poder Judicial de la Federación. «completa»: clasifica la consulta, la expande, reordena los resultados y suma las tesis del TFJA; acepta consultas de hasta 1,000 caracteres.",
            "default": "rapida"
          },
          "filtros": {
            "type": "object",
            "properties": {
              "epoca": {
                "type": "string",
                "enum": [
                  "quinta",
                  "sexta",
                  "septima",
                  "octava",
                  "novena",
                  "decima",
                  "undecima",
                  "duodecima"
                ],
                "description": "Época de la tesis. Sin este filtro la búsqueda cubre de la Novena a la Duodécima Época (y el Tribunal Electoral); la Quinta a la Octava se buscan sólo con este filtro."
              },
              "instancia": {
                "type": "string",
                "enum": [
                  "suprema_corte",
                  "plenos_regionales",
                  "plenos_de_circuito",
                  "tribunales_colegiados"
                ]
              },
              "tipo": {
                "type": "string",
                "enum": [
                  "jurisprudencia",
                  "aislada",
                  "precedente"
                ]
              }
            },
            "required": [],
            "additionalProperties": false,
            "description": "Filtros opcionales por época e instancia (del Poder Judicial de la Federación) y por tipo de tesis («precedente» sólo existe en el TFJA y pide el modo «completa»)."
          }
        },
        "required": [
          "consulta"
        ],
        "additionalProperties": false
      },
      "luthor_buscar_tesis.salida": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_buscar_tesis.salida",
        "title": "Buscar tesis: resultado",
        "type": "object",
        "properties": {
          "contrato": {
            "const": "luthor-datos/1"
          },
          "resultados": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^(?:JP-[1-9][0-9]{0,11}|TFJA-[1-9][0-9]{0,8})$"
                },
                "familia": {
                  "const": "tesis"
                },
                "url": {
                  "anyOf": [
                    {
                      "type": "string",
                      "pattern": "^https://luthor\\.mx/",
                      "maxLength": 256
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "procedencia": {
                  "type": "object",
                  "properties": {
                    "fuente": {
                      "const": "Archivo Soberano Luthor"
                    },
                    "revision": {
                      "anyOf": [
                        {
                          "type": "string",
                          "maxLength": 128
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "fecha_revision": {
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "registro": {
                      "type": "string",
                      "pattern": "^[1-9][0-9]{0,11}$"
                    },
                    "clave": {
                      "type": "string"
                    },
                    "epoca": {
                      "type": "string"
                    },
                    "instancia": {
                      "type": "string"
                    },
                    "organo": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "localizacion": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "fuente",
                    "revision",
                    "fecha_revision",
                    "registro",
                    "clave",
                    "epoca",
                    "instancia",
                    "organo",
                    "localizacion"
                  ],
                  "additionalProperties": false
                },
                "estado_tesis": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "tipo": {
                          "type": "string",
                          "enum": [
                            "jurisprudencia",
                            "aislada",
                            "precedente"
                          ]
                        },
                        "origen_tipo": {
                          "type": "string",
                          "enum": [
                            "registro_del_archivo"
                          ]
                        },
                        "interrupcion_o_superacion": {
                          "type": "string",
                          "enum": [
                            "registrada",
                            "no_registrada_en_corpus"
                          ]
                        },
                        "causa": {
                          "type": "null"
                        }
                      },
                      "required": [
                        "tipo",
                        "origen_tipo",
                        "interrupcion_o_superacion",
                        "causa"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "tipo": {
                          "const": "NO_VERIFICADO"
                        },
                        "origen_tipo": {
                          "type": "null"
                        },
                        "interrupcion_o_superacion": {
                          "type": "string",
                          "enum": [
                            "registrada",
                            "no_registrada_en_corpus"
                          ]
                        },
                        "causa": {
                          "type": "string",
                          "enum": [
                            "SIN_REGISTRO_EN_ARCHIVO",
                            "ARCHIVO_NO_DISPONIBLE"
                          ]
                        }
                      },
                      "required": [
                        "tipo",
                        "origen_tipo",
                        "interrupcion_o_superacion",
                        "causa"
                      ],
                      "additionalProperties": false
                    }
                  ]
                },
                "rubro": {
                  "type": "string"
                },
                "texto_transformado": {
                  "type": "boolean"
                },
                "extracto": {
                  "type": "string",
                  "maxLength": 1200
                },
                "truncado": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "truncado": {
                          "const": false
                        },
                        "motivo": {
                          "type": "null"
                        },
                        "fragmento": {
                          "type": "null"
                        },
                        "fragmentos_totales": {
                          "type": "null"
                        },
                        "bytes_entregados": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "bytes_totales": {
                          "type": "integer",
                          "minimum": 0
                        }
                      },
                      "required": [
                        "truncado",
                        "motivo",
                        "fragmento",
                        "fragmentos_totales",
                        "bytes_entregados",
                        "bytes_totales"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "truncado": {
                          "const": true
                        },
                        "motivo": {
                          "const": "EXTRACTO"
                        },
                        "fragmento": {
                          "type": "null"
                        },
                        "fragmentos_totales": {
                          "type": "null"
                        },
                        "bytes_entregados": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "bytes_totales": {
                          "type": "integer",
                          "minimum": 0
                        }
                      },
                      "required": [
                        "truncado",
                        "motivo",
                        "fragmento",
                        "fragmentos_totales",
                        "bytes_entregados",
                        "bytes_totales"
                      ],
                      "additionalProperties": false
                    }
                  ]
                },
                "posicion": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 10
                },
                "puntaje": {
                  "type": "number"
                },
                "criterio_ranking": {
                  "type": "string",
                  "enum": [
                    "semantico",
                    "semantico_reordenado",
                    "hibrido"
                  ]
                },
                "coleccion": {
                  "type": "string",
                  "enum": [
                    "curado",
                    "archivo",
                    "tfja"
                  ]
                },
                "peso": {
                  "type": "string",
                  "minLength": 1
                },
                "situacion": {
                  "type": "string",
                  "minLength": 1
                }
              },
              "required": [
                "id",
                "familia",
                "url",
                "procedencia",
                "estado_tesis",
                "rubro",
                "texto_transformado",
                "extracto",
                "truncado",
                "posicion",
                "puntaje",
                "criterio_ranking"
              ],
              "additionalProperties": false
            }
          },
          "modo_efectivo": {
            "type": "string",
            "enum": [
              "rapida",
              "completa"
            ]
          },
          "truncado": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "truncado": {
                    "const": false
                  },
                  "motivo": {
                    "type": "null"
                  },
                  "resultados_entregados": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10
                  },
                  "resultados_candidatos": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "extracto_max_caracteres": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1200
                  }
                },
                "required": [
                  "truncado",
                  "motivo",
                  "resultados_entregados",
                  "resultados_candidatos",
                  "extracto_max_caracteres"
                ],
                "additionalProperties": false
              },
              {
                "type": "object",
                "properties": {
                  "truncado": {
                    "const": true
                  },
                  "motivo": {
                    "type": "string",
                    "enum": [
                      "TECHO_DE_SALIDA"
                    ]
                  },
                  "resultados_entregados": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10
                  },
                  "resultados_candidatos": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "extracto_max_caracteres": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1200
                  }
                },
                "required": [
                  "truncado",
                  "motivo",
                  "resultados_entregados",
                  "resultados_candidatos",
                  "extracto_max_caracteres"
                ],
                "additionalProperties": false
              }
            ]
          },
          "corpus": {
            "type": "object",
            "properties": {
              "coleccion": {
                "type": "string",
                "enum": [
                  "curado",
                  "archivo"
                ]
              },
              "corte": {
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "revision": {
                "anyOf": [
                  {
                    "type": "string",
                    "maxLength": 128
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "coleccion",
              "corte",
              "revision"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "contrato",
          "resultados",
          "modo_efectivo",
          "truncado",
          "corpus"
        ],
        "additionalProperties": false
      },
      "luthor_leer_tesis.entrada": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_leer_tesis.entrada",
        "title": "Leer tesis: entrada",
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^(?:JP-[1-9][0-9]{0,11}|TFJA-[1-9][0-9]{0,8})$",
            "description": "Identificador de la tesis, p. ej. JP-159807 (Poder Judicial de la Federación) o TFJA-48288 (Tribunal Federal de Justicia Administrativa)."
          },
          "fragmento": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "description": "Fragmento del documento, base 1. Los fragmentos son fijos por documento; cada uno es una lectura.",
            "default": 1
          }
        },
        "required": [
          "id"
        ],
        "additionalProperties": false
      },
      "luthor_leer_tesis.salida": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_leer_tesis.salida",
        "title": "Leer tesis: resultado",
        "type": "object",
        "properties": {
          "contrato": {
            "const": "luthor-datos/1"
          },
          "documento": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^(?:JP-[1-9][0-9]{0,11}|TFJA-[1-9][0-9]{0,8})$"
              },
              "familia": {
                "const": "tesis"
              },
              "url": {
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^https://luthor\\.mx/",
                    "maxLength": 256
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "procedencia": {
                "type": "object",
                "properties": {
                  "fuente": {
                    "const": "Archivo Soberano Luthor"
                  },
                  "revision": {
                    "anyOf": [
                      {
                        "type": "string",
                        "maxLength": 128
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "fecha_revision": {
                    "anyOf": [
                      {
                        "type": "string",
                        "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "registro": {
                    "type": "string",
                    "pattern": "^[1-9][0-9]{0,11}$"
                  },
                  "clave": {
                    "type": "string"
                  },
                  "epoca": {
                    "type": "string"
                  },
                  "instancia": {
                    "type": "string"
                  },
                  "organo": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "localizacion": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "fuente",
                  "revision",
                  "fecha_revision",
                  "registro",
                  "clave",
                  "epoca",
                  "instancia",
                  "organo",
                  "localizacion"
                ],
                "additionalProperties": false
              },
              "estado_tesis": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "tipo": {
                        "type": "string",
                        "enum": [
                          "jurisprudencia",
                          "aislada",
                          "precedente"
                        ]
                      },
                      "origen_tipo": {
                        "type": "string",
                        "enum": [
                          "registro_del_archivo"
                        ]
                      },
                      "interrupcion_o_superacion": {
                        "type": "string",
                        "enum": [
                          "registrada",
                          "no_registrada_en_corpus"
                        ]
                      },
                      "causa": {
                        "type": "null"
                      }
                    },
                    "required": [
                      "tipo",
                      "origen_tipo",
                      "interrupcion_o_superacion",
                      "causa"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "object",
                    "properties": {
                      "tipo": {
                        "const": "NO_VERIFICADO"
                      },
                      "origen_tipo": {
                        "type": "null"
                      },
                      "interrupcion_o_superacion": {
                        "type": "string",
                        "enum": [
                          "registrada",
                          "no_registrada_en_corpus"
                        ]
                      },
                      "causa": {
                        "type": "string",
                        "enum": [
                          "SIN_REGISTRO_EN_ARCHIVO",
                          "ARCHIVO_NO_DISPONIBLE"
                        ]
                      }
                    },
                    "required": [
                      "tipo",
                      "origen_tipo",
                      "interrupcion_o_superacion",
                      "causa"
                    ],
                    "additionalProperties": false
                  }
                ]
              },
              "rubro": {
                "type": "string"
              },
              "texto_transformado": {
                "type": "boolean"
              },
              "texto": {
                "type": "string"
              },
              "truncado": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "truncado": {
                        "const": false
                      },
                      "motivo": {
                        "type": "null"
                      },
                      "fragmento": {
                        "const": 1
                      },
                      "fragmentos_totales": {
                        "const": 1
                      },
                      "bytes_entregados": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "bytes_totales": {
                        "type": "integer",
                        "minimum": 0
                      }
                    },
                    "required": [
                      "truncado",
                      "motivo",
                      "fragmento",
                      "fragmentos_totales",
                      "bytes_entregados",
                      "bytes_totales"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "object",
                    "properties": {
                      "truncado": {
                        "const": true
                      },
                      "motivo": {
                        "const": "FRAGMENTADO"
                      },
                      "fragmento": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "fragmentos_totales": {
                        "type": "integer",
                        "minimum": 2
                      },
                      "bytes_entregados": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "bytes_totales": {
                        "type": "integer",
                        "minimum": 0
                      }
                    },
                    "required": [
                      "truncado",
                      "motivo",
                      "fragmento",
                      "fragmentos_totales",
                      "bytes_entregados",
                      "bytes_totales"
                    ],
                    "additionalProperties": false
                  }
                ]
              },
              "coleccion": {
                "type": "string",
                "enum": [
                  "curado",
                  "archivo",
                  "tfja"
                ]
              },
              "peso": {
                "type": "string",
                "minLength": 1
              },
              "situacion": {
                "type": "string",
                "minLength": 1
              }
            },
            "required": [
              "id",
              "familia",
              "url",
              "procedencia",
              "estado_tesis",
              "rubro",
              "texto_transformado",
              "texto",
              "truncado"
            ],
            "additionalProperties": false
          },
          "corpus": {
            "type": "object",
            "properties": {
              "coleccion": {
                "type": "string",
                "enum": [
                  "curado",
                  "archivo"
                ]
              },
              "corte": {
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "revision": {
                "anyOf": [
                  {
                    "type": "string",
                    "maxLength": 128
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "coleccion",
              "corte",
              "revision"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "contrato",
          "documento",
          "corpus"
        ],
        "additionalProperties": false
      },
      "luthor_buscar_articulo.entrada": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_buscar_articulo.entrada",
        "title": "Buscar artículos: entrada",
        "type": "object",
        "properties": {
          "consulta": {
            "type": "string",
            "description": "Texto a buscar, en español.",
            "minLength": 1,
            "maxLength": 4000,
            "pattern": "\\S"
          },
          "limite": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10,
            "description": "Cuántos resultados devolver.",
            "default": 5
          },
          "ley": {
            "type": "string",
            "enum": [
              "CFF",
              "LISR",
              "LIVA",
              "LFPCA",
              "RCFF",
              "RLISR",
              "RLIVA",
              "RMF-2026",
              "LAMP",
              "LOTFJA",
              "CPEUM",
              "RISAT",
              "LFT",
              "CNPP",
              "CPF",
              "CCOM"
            ],
            "description": "Código de ley del catálogo de 16 ordenamientos."
          }
        },
        "required": [
          "consulta"
        ],
        "additionalProperties": false
      },
      "luthor_buscar_articulo.salida": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_buscar_articulo.salida",
        "title": "Buscar artículos: resultado",
        "type": "object",
        "properties": {
          "contrato": {
            "const": "luthor-datos/1"
          },
          "resultados": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "maxLength": 64,
                  "pattern": "^(?:[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*-(?:art|cap)-[A-Za-z0-9.]+(?:-[A-Za-z0-9]+)*|[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*-transitorios|RMF-[0-9]{4}-00-glosario|RMF-[0-9]{4}-01-tit-[0-9]+|RMF-[0-9]{4}-anexo-[0-9]+|decreto(?:-[a-z0-9]+)+)$",
                  "not": {
                    "pattern": "\\.\\."
                  }
                },
                "familia": {
                  "const": "articulo"
                },
                "url": {
                  "anyOf": [
                    {
                      "type": "string",
                      "pattern": "^https://luthor\\.mx/",
                      "maxLength": 256
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "procedencia": {
                  "type": "object",
                  "properties": {
                    "fuente": {
                      "const": "Archivo Soberano Luthor"
                    },
                    "revision": {
                      "anyOf": [
                        {
                          "type": "string",
                          "maxLength": 128
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "fecha_revision": {
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "ley": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "codigo_ley": {
                      "anyOf": [
                        {
                          "type": "string",
                          "enum": [
                            "CFF",
                            "LISR",
                            "LIVA",
                            "LFPCA",
                            "RCFF",
                            "RLISR",
                            "RLIVA",
                            "RMF-2026",
                            "LAMP",
                            "LOTFJA",
                            "CPEUM",
                            "RISAT",
                            "LFT",
                            "CNPP",
                            "CPF",
                            "CCOM"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "numero": {
                      "type": "string",
                      "maxLength": 64
                    }
                  },
                  "required": [
                    "fuente",
                    "revision",
                    "fecha_revision",
                    "ley",
                    "codigo_ley",
                    "numero"
                  ],
                  "additionalProperties": false
                },
                "vigencia": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "estado": {
                          "const": "vigente"
                        },
                        "fuente": {
                          "type": "string",
                          "enum": [
                            "texto_servido",
                            "bitacora_de_revisiones",
                            "cotejo_con_archivo"
                          ]
                        },
                        "causa": {
                          "type": "null"
                        },
                        "granularidad": {
                          "const": "articulo"
                        },
                        "fecha_corte": {
                          "type": "string",
                          "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                        }
                      },
                      "required": [
                        "estado",
                        "fuente",
                        "causa",
                        "granularidad",
                        "fecha_corte"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "estado": {
                          "const": "derogado"
                        },
                        "fuente": {
                          "type": "string",
                          "enum": [
                            "texto_servido",
                            "bitacora_de_revisiones"
                          ]
                        },
                        "causa": {
                          "type": "null"
                        },
                        "granularidad": {
                          "const": "articulo"
                        },
                        "fecha_corte": {
                          "anyOf": [
                            {
                              "type": "string",
                              "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "estado",
                        "fuente",
                        "causa",
                        "granularidad",
                        "fecha_corte"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "estado": {
                          "const": "NO_VERIFICADO"
                        },
                        "fuente": {
                          "type": "null"
                        },
                        "causa": {
                          "type": "string",
                          "enum": [
                            "SIN_HISTORIAL",
                            "VIGENCIA_SOLO_POR_LEY",
                            "AVISO_DIFERIDO_PENDIENTE",
                            "DIFIERE_DEL_ARCHIVO",
                            "COTEJO_DESACTUALIZADO"
                          ]
                        },
                        "granularidad": {
                          "type": "string",
                          "enum": [
                            "articulo",
                            "ley"
                          ]
                        },
                        "fecha_corte": {
                          "anyOf": [
                            {
                              "type": "string",
                              "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "estado",
                        "fuente",
                        "causa",
                        "granularidad",
                        "fecha_corte"
                      ],
                      "additionalProperties": false
                    }
                  ]
                },
                "titulo": {
                  "type": "string"
                },
                "texto_transformado": {
                  "type": "boolean"
                },
                "extracto": {
                  "type": "string",
                  "maxLength": 1200
                },
                "truncado": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "truncado": {
                          "const": false
                        },
                        "motivo": {
                          "type": "null"
                        },
                        "fragmento": {
                          "type": "null"
                        },
                        "fragmentos_totales": {
                          "type": "null"
                        },
                        "bytes_entregados": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "bytes_totales": {
                          "type": "integer",
                          "minimum": 0
                        }
                      },
                      "required": [
                        "truncado",
                        "motivo",
                        "fragmento",
                        "fragmentos_totales",
                        "bytes_entregados",
                        "bytes_totales"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "truncado": {
                          "const": true
                        },
                        "motivo": {
                          "const": "EXTRACTO"
                        },
                        "fragmento": {
                          "type": "null"
                        },
                        "fragmentos_totales": {
                          "type": "null"
                        },
                        "bytes_entregados": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "bytes_totales": {
                          "type": "integer",
                          "minimum": 0
                        }
                      },
                      "required": [
                        "truncado",
                        "motivo",
                        "fragmento",
                        "fragmentos_totales",
                        "bytes_entregados",
                        "bytes_totales"
                      ],
                      "additionalProperties": false
                    }
                  ]
                },
                "posicion": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 10
                },
                "puntaje": {
                  "type": "number"
                },
                "criterio_ranking": {
                  "const": "lexico_posicional"
                }
              },
              "required": [
                "id",
                "familia",
                "url",
                "procedencia",
                "vigencia",
                "titulo",
                "texto_transformado",
                "extracto",
                "truncado",
                "posicion",
                "puntaje",
                "criterio_ranking"
              ],
              "additionalProperties": false
            }
          },
          "modo_efectivo": {
            "const": "rapida"
          },
          "truncado": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "truncado": {
                    "const": false
                  },
                  "motivo": {
                    "type": "null"
                  },
                  "resultados_entregados": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10
                  },
                  "resultados_candidatos": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "extracto_max_caracteres": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1200
                  }
                },
                "required": [
                  "truncado",
                  "motivo",
                  "resultados_entregados",
                  "resultados_candidatos",
                  "extracto_max_caracteres"
                ],
                "additionalProperties": false
              },
              {
                "type": "object",
                "properties": {
                  "truncado": {
                    "const": true
                  },
                  "motivo": {
                    "type": "string",
                    "enum": [
                      "TECHO_DE_SALIDA"
                    ]
                  },
                  "resultados_entregados": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10
                  },
                  "resultados_candidatos": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "extracto_max_caracteres": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1200
                  }
                },
                "required": [
                  "truncado",
                  "motivo",
                  "resultados_entregados",
                  "resultados_candidatos",
                  "extracto_max_caracteres"
                ],
                "additionalProperties": false
              }
            ]
          },
          "corpus": {
            "type": "object",
            "properties": {
              "coleccion": {
                "type": "string",
                "enum": [
                  "curado",
                  "archivo"
                ]
              },
              "corte": {
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "revision": {
                "anyOf": [
                  {
                    "type": "string",
                    "maxLength": 128
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "coleccion",
              "corte",
              "revision"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "contrato",
          "resultados",
          "modo_efectivo",
          "truncado",
          "corpus"
        ],
        "additionalProperties": false
      },
      "luthor_leer_articulo.entrada": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_leer_articulo.entrada",
        "title": "Leer artículo: entrada",
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^(?:[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*-(?:art|cap)-[A-Za-z0-9.]+(?:-[A-Za-z0-9]+)*|[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*-transitorios|RMF-[0-9]{4}-00-glosario|RMF-[0-9]{4}-01-tit-[0-9]+|RMF-[0-9]{4}-anexo-[0-9]+|decreto(?:-[a-z0-9]+)+)$",
            "not": {
              "pattern": "\\.\\."
            },
            "description": "Identificador del artículo, p. ej. CFF-art-17-H-Bis o RMF-2026-cap-2.1.1."
          },
          "ley": {
            "type": "string",
            "enum": [
              "CFF",
              "LISR",
              "LIVA",
              "LFPCA",
              "RCFF",
              "RLISR",
              "RLIVA",
              "RMF-2026",
              "LAMP",
              "LOTFJA",
              "CPEUM",
              "RISAT",
              "LFT",
              "CNPP",
              "CPF",
              "CCOM"
            ],
            "description": "Código de ley del catálogo de 16 ordenamientos. Va con `articulo` y sin `id`."
          },
          "articulo": {
            "type": "string",
            "description": "Número del artículo o de la regla, p. ej. 42 o 2.1.1. Va con `ley` y sin `id`.",
            "minLength": 1,
            "maxLength": 40,
            "pattern": "^[0-9A-Za-z][0-9A-Za-z.-]*$"
          },
          "fragmento": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "description": "Fragmento del documento, base 1. Los fragmentos son fijos por documento; cada uno es una lectura.",
            "default": 1
          }
        },
        "required": [],
        "additionalProperties": false,
        "description": "Pasa `id`, o bien `ley` y `articulo`, pero no ambos."
      },
      "luthor_leer_articulo.salida": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_leer_articulo.salida",
        "title": "Leer artículo: resultado",
        "type": "object",
        "properties": {
          "contrato": {
            "const": "luthor-datos/1"
          },
          "documento": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^(?:[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*-(?:art|cap)-[A-Za-z0-9.]+(?:-[A-Za-z0-9]+)*|[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*-transitorios|RMF-[0-9]{4}-00-glosario|RMF-[0-9]{4}-01-tit-[0-9]+|RMF-[0-9]{4}-anexo-[0-9]+|decreto(?:-[a-z0-9]+)+)$",
                "not": {
                  "pattern": "\\.\\."
                }
              },
              "familia": {
                "const": "articulo"
              },
              "url": {
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^https://luthor\\.mx/",
                    "maxLength": 256
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "procedencia": {
                "type": "object",
                "properties": {
                  "fuente": {
                    "const": "Archivo Soberano Luthor"
                  },
                  "revision": {
                    "anyOf": [
                      {
                        "type": "string",
                        "maxLength": 128
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "fecha_revision": {
                    "anyOf": [
                      {
                        "type": "string",
                        "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "ley": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "codigo_ley": {
                    "anyOf": [
                      {
                        "type": "string",
                        "enum": [
                          "CFF",
                          "LISR",
                          "LIVA",
                          "LFPCA",
                          "RCFF",
                          "RLISR",
                          "RLIVA",
                          "RMF-2026",
                          "LAMP",
                          "LOTFJA",
                          "CPEUM",
                          "RISAT",
                          "LFT",
                          "CNPP",
                          "CPF",
                          "CCOM"
                        ]
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "numero": {
                    "type": "string",
                    "maxLength": 64
                  }
                },
                "required": [
                  "fuente",
                  "revision",
                  "fecha_revision",
                  "ley",
                  "codigo_ley",
                  "numero"
                ],
                "additionalProperties": false
              },
              "vigencia": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "estado": {
                        "const": "vigente"
                      },
                      "fuente": {
                        "type": "string",
                        "enum": [
                          "texto_servido",
                          "bitacora_de_revisiones"
                        ]
                      },
                      "causa": {
                        "type": "null"
                      },
                      "granularidad": {
                        "const": "articulo"
                      },
                      "fecha_corte": {
                        "type": "string",
                        "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                      }
                    },
                    "required": [
                      "estado",
                      "fuente",
                      "causa",
                      "granularidad",
                      "fecha_corte"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "object",
                    "properties": {
                      "estado": {
                        "const": "vigente"
                      },
                      "fuente": {
                        "const": "cotejo_con_archivo"
                      },
                      "causa": {
                        "type": "null"
                      },
                      "granularidad": {
                        "const": "articulo"
                      },
                      "fecha_corte": {
                        "type": "string",
                        "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                      },
                      "ultima_reforma": {
                        "anyOf": [
                          {
                            "type": "string",
                            "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "version_cotejada": {
                        "type": "string",
                        "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                      }
                    },
                    "required": [
                      "estado",
                      "fuente",
                      "causa",
                      "granularidad",
                      "fecha_corte",
                      "ultima_reforma",
                      "version_cotejada"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "object",
                    "properties": {
                      "estado": {
                        "const": "derogado"
                      },
                      "fuente": {
                        "type": "string",
                        "enum": [
                          "texto_servido",
                          "bitacora_de_revisiones"
                        ]
                      },
                      "causa": {
                        "type": "null"
                      },
                      "granularidad": {
                        "const": "articulo"
                      },
                      "fecha_corte": {
                        "anyOf": [
                          {
                            "type": "string",
                            "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "estado",
                      "fuente",
                      "causa",
                      "granularidad",
                      "fecha_corte"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "object",
                    "properties": {
                      "estado": {
                        "const": "NO_VERIFICADO"
                      },
                      "fuente": {
                        "type": "null"
                      },
                      "causa": {
                        "type": "string",
                        "enum": [
                          "SIN_HISTORIAL",
                          "VIGENCIA_SOLO_POR_LEY",
                          "AVISO_DIFERIDO_PENDIENTE",
                          "DIFIERE_DEL_ARCHIVO",
                          "COTEJO_DESACTUALIZADO"
                        ]
                      },
                      "granularidad": {
                        "type": "string",
                        "enum": [
                          "articulo",
                          "ley"
                        ]
                      },
                      "fecha_corte": {
                        "anyOf": [
                          {
                            "type": "string",
                            "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "version_cotejada": {
                        "type": "string",
                        "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                      }
                    },
                    "required": [
                      "estado",
                      "fuente",
                      "causa",
                      "granularidad",
                      "fecha_corte"
                    ],
                    "additionalProperties": false
                  }
                ]
              },
              "titulo": {
                "type": "string"
              },
              "texto_transformado": {
                "type": "boolean"
              },
              "texto": {
                "type": "string"
              },
              "truncado": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "truncado": {
                        "const": false
                      },
                      "motivo": {
                        "type": "null"
                      },
                      "fragmento": {
                        "const": 1
                      },
                      "fragmentos_totales": {
                        "const": 1
                      },
                      "bytes_entregados": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "bytes_totales": {
                        "type": "integer",
                        "minimum": 0
                      }
                    },
                    "required": [
                      "truncado",
                      "motivo",
                      "fragmento",
                      "fragmentos_totales",
                      "bytes_entregados",
                      "bytes_totales"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "object",
                    "properties": {
                      "truncado": {
                        "const": true
                      },
                      "motivo": {
                        "const": "FRAGMENTADO"
                      },
                      "fragmento": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "fragmentos_totales": {
                        "type": "integer",
                        "minimum": 2
                      },
                      "bytes_entregados": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "bytes_totales": {
                        "type": "integer",
                        "minimum": 0
                      }
                    },
                    "required": [
                      "truncado",
                      "motivo",
                      "fragmento",
                      "fragmentos_totales",
                      "bytes_entregados",
                      "bytes_totales"
                    ],
                    "additionalProperties": false
                  }
                ]
              }
            },
            "required": [
              "id",
              "familia",
              "url",
              "procedencia",
              "vigencia",
              "titulo",
              "texto_transformado",
              "texto",
              "truncado"
            ],
            "additionalProperties": false
          },
          "corpus": {
            "type": "object",
            "properties": {
              "coleccion": {
                "type": "string",
                "enum": [
                  "curado",
                  "archivo"
                ]
              },
              "corte": {
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "revision": {
                "anyOf": [
                  {
                    "type": "string",
                    "maxLength": 128
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "coleccion",
              "corte",
              "revision"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "contrato",
          "documento",
          "corpus"
        ],
        "additionalProperties": false
      },
      "luthor_buscar_ordenamiento.entrada": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_buscar_ordenamiento.entrada",
        "title": "Buscar ordenamientos: entrada",
        "type": "object",
        "properties": {
          "consulta": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "pattern": "\\S",
            "description": "Palabras del nombre de la norma o de su materia (p. ej. «código civil», «ley de hacienda municipal»), de 1 a 200 caracteres. Hoy se busca en la ficha del catálogo (título, categoría y materia)."
          },
          "limite": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10,
            "description": "Cuántos resultados devolver.",
            "default": 5
          },
          "filtros": {
            "type": "object",
            "properties": {
              "ambito": {
                "type": "string",
                "enum": [
                  "federal",
                  "estatal",
                  "tratados"
                ],
                "description": "Federal, estatal o tratados internacionales."
              },
              "entidad": {
                "type": "string",
                "enum": [
                  "aguascalientes",
                  "baja_california",
                  "baja_california_sur",
                  "campeche",
                  "chiapas",
                  "chihuahua",
                  "ciudad_de_mexico",
                  "coahuila",
                  "colima",
                  "durango",
                  "estado_de_mexico",
                  "guanajuato",
                  "guerrero",
                  "hidalgo",
                  "jalisco",
                  "michoacan",
                  "morelos",
                  "nayarit",
                  "nuevo_leon",
                  "oaxaca",
                  "puebla",
                  "queretaro",
                  "quintana_roo",
                  "san_luis_potosi",
                  "sinaloa",
                  "sonora",
                  "tabasco",
                  "tamaulipas",
                  "tlaxcala",
                  "veracruz",
                  "yucatan",
                  "zacatecas"
                ],
                "description": "Entidad federativa (sólo normas estatales). Hay una norma homónima por entidad: úsalo cuando la consulta nombra un estado."
              },
              "categoria": {
                "type": "string",
                "enum": [
                  "ley",
                  "reglamento",
                  "codigo",
                  "constitucion",
                  "decreto",
                  "acuerdo",
                  "presupuesto",
                  "plan",
                  "programa",
                  "manual",
                  "bando",
                  "estatuto",
                  "lineamientos",
                  "convenio",
                  "convencion",
                  "tratado",
                  "protocolo",
                  "declaratoria"
                ],
                "description": "Categoría de la norma."
              },
              "vigencia": {
                "type": "string",
                "enum": [
                  "vigente",
                  "no_vigente",
                  "abrogado",
                  "derogado",
                  "sin_efecto"
                ],
                "description": "Estado de vigencia DECLARADO de la norma (la etiqueta del catálogo a su fecha de corte, no verificada)."
              }
            },
            "required": [],
            "additionalProperties": false,
            "description": "Filtros opcionales por ámbito, entidad federativa, categoría y vigencia declarada."
          }
        },
        "required": [
          "consulta"
        ],
        "additionalProperties": false
      },
      "luthor_buscar_ordenamiento.salida": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_buscar_ordenamiento.salida",
        "title": "Buscar ordenamientos: resultado",
        "type": "object",
        "properties": {
          "contrato": {
            "const": "luthor-datos/1"
          },
          "resultados": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^ORD-(?:0|[1-9][0-9]{0,11})$",
                  "description": "Identificador de la norma (`ORD-{id}`): la ÚNICA identidad (hay títulos homónimos). Úsalo para leerla."
                },
                "familia": {
                  "const": "ordenamiento",
                  "description": "`ordenamiento`."
                },
                "url": {
                  "anyOf": [
                    {
                      "type": "string",
                      "pattern": "^https://luthor\\.mx/",
                      "maxLength": 256
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "Página pública de la norma (o de la versión leída) en luthor.mx."
                },
                "procedencia": {
                  "type": "object",
                  "properties": {
                    "fuente": {
                      "const": "Archivo Soberano Luthor"
                    },
                    "revision": {
                      "anyOf": [
                        {
                          "type": "string",
                          "maxLength": 128
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "La versión leída (su `reforma_id`) o, en una búsqueda, `null`."
                    },
                    "fecha_revision": {
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "Fecha de publicación de la versión leída, o `null`."
                    },
                    "ambito": {
                      "type": "string",
                      "enum": [
                        "federal",
                        "estatal",
                        "tratados"
                      ],
                      "description": "`federal`, `estatal` o `tratados` (internacionales)."
                    },
                    "entidad": {
                      "anyOf": [
                        {
                          "type": "string",
                          "enum": [
                            "aguascalientes",
                            "baja_california",
                            "baja_california_sur",
                            "campeche",
                            "chiapas",
                            "chihuahua",
                            "ciudad_de_mexico",
                            "coahuila",
                            "colima",
                            "durango",
                            "estado_de_mexico",
                            "guanajuato",
                            "guerrero",
                            "hidalgo",
                            "jalisco",
                            "michoacan",
                            "morelos",
                            "nayarit",
                            "nuevo_leon",
                            "oaxaca",
                            "puebla",
                            "queretaro",
                            "quintana_roo",
                            "san_luis_potosi",
                            "sinaloa",
                            "sonora",
                            "tabasco",
                            "tamaulipas",
                            "tlaxcala",
                            "veracruz",
                            "yucatan",
                            "zacatecas"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "La entidad federativa de una norma estatal; `null` en federal y tratados."
                    },
                    "categoria": {
                      "anyOf": [
                        {
                          "type": "string",
                          "maxLength": 64
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "La categoría tal como la trae el catálogo (LEY, REGLAMENTO, CODIGO…), o `null`."
                    }
                  },
                  "required": [
                    "fuente",
                    "revision",
                    "fecha_revision",
                    "ambito",
                    "entidad",
                    "categoria"
                  ],
                  "additionalProperties": false,
                  "description": "De dónde sale la norma. La fuente es siempre «Archivo Soberano Luthor»."
                },
                "titulo": {
                  "type": "string",
                  "description": "Título de la norma, como lo trae el catálogo."
                },
                "vigencia_ordenamiento": {
                  "description": "Qué se puede afirmar de la vigencia de la NORMA completa, con su fuente y su fecha de corte. No se refiere a un artículo.",
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "estado": {
                          "const": "vigente",
                          "description": "`vigente`, `no_vigente`, `abrogado`, `derogado`, `sin_efecto`, `indeterminado` (dos evidencias que chocan) o `desconocido`. «vigente» siempre con fuente y fecha de corte."
                        },
                        "fuente": {
                          "type": "string",
                          "enum": [
                            "catalogo_del_archivo",
                            "regla_ejercicio",
                            "abrogacion_con_cita"
                          ],
                          "description": "De dónde sale el estado: `catalogo_del_archivo` (la etiqueta del catálogo del Archivo Soberano Luthor a su fecha de corte), `regla_ejercicio` (una ley de ingresos o un presupuesto de un ejercicio concluido) o `abrogacion_con_cita` (una norma posterior que la abroga, citada)."
                        },
                        "fecha_corte": {
                          "type": "string",
                          "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$",
                          "description": "Fecha de negocio (AAAA-MM-DD) de la evidencia (hoy, el censo del catálogo); `null` si no la hay."
                        },
                        "fecha_efecto": {
                          "anyOf": [
                            {
                              "type": "string",
                              "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$",
                              "description": "Fecha de negocio (AAAA-MM-DD) en que el estado surtió efecto, o `null` si no se conoce."
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "confianza": {
                          "type": "string",
                          "enum": [
                            "alta",
                            "media",
                            "baja"
                          ],
                          "description": "`alta` = verificada; `media` o `baja` = registrada, no verificada."
                        },
                        "etiqueta_archivo": {
                          "anyOf": [
                            {
                              "type": "string",
                              "maxLength": 64,
                              "description": "La etiqueta de vigencia tal como la trae el catálogo, por transparencia, o `null`."
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "estado",
                        "fuente",
                        "fecha_corte",
                        "fecha_efecto",
                        "confianza",
                        "etiqueta_archivo"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "estado": {
                          "type": "string",
                          "enum": [
                            "no_vigente",
                            "abrogado",
                            "derogado",
                            "sin_efecto",
                            "indeterminado",
                            "desconocido"
                          ],
                          "description": "`vigente`, `no_vigente`, `abrogado`, `derogado`, `sin_efecto`, `indeterminado` (dos evidencias que chocan) o `desconocido`. «vigente» siempre con fuente y fecha de corte."
                        },
                        "fuente": {
                          "type": "string",
                          "enum": [
                            "catalogo_del_archivo",
                            "regla_ejercicio",
                            "abrogacion_con_cita"
                          ],
                          "description": "De dónde sale el estado: `catalogo_del_archivo` (la etiqueta del catálogo del Archivo Soberano Luthor a su fecha de corte), `regla_ejercicio` (una ley de ingresos o un presupuesto de un ejercicio concluido) o `abrogacion_con_cita` (una norma posterior que la abroga, citada)."
                        },
                        "fecha_corte": {
                          "anyOf": [
                            {
                              "type": "string",
                              "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "Fecha de negocio (AAAA-MM-DD) de la evidencia (hoy, el censo del catálogo); `null` si no la hay."
                        },
                        "fecha_efecto": {
                          "anyOf": [
                            {
                              "type": "string",
                              "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$",
                              "description": "Fecha de negocio (AAAA-MM-DD) en que el estado surtió efecto, o `null` si no se conoce."
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "confianza": {
                          "type": "string",
                          "enum": [
                            "alta",
                            "media",
                            "baja"
                          ],
                          "description": "`alta` = verificada; `media` o `baja` = registrada, no verificada."
                        },
                        "etiqueta_archivo": {
                          "anyOf": [
                            {
                              "type": "string",
                              "maxLength": 64,
                              "description": "La etiqueta de vigencia tal como la trae el catálogo, por transparencia, o `null`."
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "estado",
                        "fuente",
                        "fecha_corte",
                        "fecha_efecto",
                        "confianza",
                        "etiqueta_archivo"
                      ],
                      "additionalProperties": false
                    }
                  ]
                },
                "version_vigente": {
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "reforma_id": {
                          "type": "string",
                          "pattern": "^(?:0|[1-9][0-9]{0,11})$",
                          "description": "Identificador de la versión (úsalo en `ORD-{id}@{reforma_id}`)."
                        },
                        "fecha_publicacion": {
                          "anyOf": [
                            {
                              "type": "string",
                              "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "Fecha de publicación de la versión (AAAA-MM-DD), o `null`."
                        },
                        "regla": {
                          "type": "string",
                          "enum": [
                            "censo",
                            "fecha_v1"
                          ],
                          "description": "Cómo se eligió: `censo` (el puntero del catálogo del archivo) o `fecha_v1` (la última publicada con fecha hasta hoy)."
                        },
                        "confianza": {
                          "type": "string",
                          "enum": [
                            "alta",
                            "media",
                            "baja"
                          ],
                          "description": "`alta` = verificada; `media` o `baja` = registrada, no verificada."
                        },
                        "texto": {
                          "type": "string",
                          "enum": [
                            "articulado",
                            "facsimil",
                            "sin_texto"
                          ],
                          "description": "Qué texto tiene esa versión: `articulado` (legible), `facsimil` (sólo imagen: no se puede leer) o `sin_texto`."
                        },
                        "ultima_con_articulado": {
                          "type": "object",
                          "properties": {
                            "reforma_id": {
                              "type": "string",
                              "pattern": "^(?:0|[1-9][0-9]{0,11})$",
                              "description": "Identificador de esa versión."
                            },
                            "fecha_publicacion": {
                              "anyOf": [
                                {
                                  "type": "string",
                                  "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                                },
                                {
                                  "type": "null"
                                }
                              ],
                              "description": "Su fecha de publicación, o `null`."
                            }
                          },
                          "required": [
                            "reforma_id",
                            "fecha_publicacion"
                          ],
                          "additionalProperties": false,
                          "description": "Opcional: la última versión con articulado legible, cuando la vigente no lo tiene."
                        },
                        "proxima": {
                          "type": "object",
                          "properties": {
                            "reforma_id": {
                              "type": "string",
                              "pattern": "^(?:0|[1-9][0-9]{0,11})$",
                              "description": "Identificador de esa versión."
                            },
                            "entrada_en_vigor": {
                              "type": "string",
                              "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$",
                              "description": "Fecha en que entra en vigor (AAAA-MM-DD)."
                            }
                          },
                          "required": [
                            "reforma_id",
                            "entrada_en_vigor"
                          ],
                          "additionalProperties": false,
                          "description": "Opcional: una versión publicada que aún no entra en vigor."
                        },
                        "empate": {
                          "type": "array",
                          "minItems": 1,
                          "maxItems": 20,
                          "items": {
                            "type": "string",
                            "pattern": "^(?:0|[1-9][0-9]{0,11})$"
                          },
                          "description": "Opcional: otras versiones publicadas el mismo día que la vigente."
                        }
                      },
                      "required": [
                        "reforma_id",
                        "fecha_publicacion",
                        "regla",
                        "confianza",
                        "texto"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "La versión que rige hoy según el catálogo, con el texto que tiene; `null` si el catálogo no la determina."
                },
                "texto_transformado": {
                  "type": "boolean"
                },
                "posicion": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 10
                },
                "puntaje": {
                  "type": "number"
                },
                "criterio_ranking": {
                  "const": "lexico_catalogo",
                  "description": "`lexico_catalogo`: coincidencia de palabras en la ficha del catálogo; el puntaje es 1/posición y sólo ordena."
                }
              },
              "required": [
                "id",
                "familia",
                "url",
                "procedencia",
                "titulo",
                "vigencia_ordenamiento",
                "version_vigente",
                "texto_transformado",
                "posicion",
                "puntaje",
                "criterio_ranking"
              ],
              "additionalProperties": false
            },
            "description": "Normas en orden de `posicion`, sin extracto. Una búsqueda sin resultados devuelve la lista vacía y sí se cobra."
          },
          "modo_efectivo": {
            "const": "rapida",
            "description": "`rapida`: la búsqueda de normas no usa modelos de lenguaje."
          },
          "truncado": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "truncado": {
                    "const": false
                  },
                  "motivo": {
                    "type": "null"
                  },
                  "resultados_entregados": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10
                  },
                  "resultados_candidatos": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "extracto_max_caracteres": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1200
                  }
                },
                "required": [
                  "truncado",
                  "motivo",
                  "resultados_entregados",
                  "resultados_candidatos",
                  "extracto_max_caracteres"
                ],
                "additionalProperties": false
              },
              {
                "type": "object",
                "properties": {
                  "truncado": {
                    "const": true
                  },
                  "motivo": {
                    "type": "string",
                    "enum": [
                      "TECHO_DE_SALIDA"
                    ]
                  },
                  "resultados_entregados": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10
                  },
                  "resultados_candidatos": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "extracto_max_caracteres": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1200
                  }
                },
                "required": [
                  "truncado",
                  "motivo",
                  "resultados_entregados",
                  "resultados_candidatos",
                  "extracto_max_caracteres"
                ],
                "additionalProperties": false
              }
            ]
          },
          "corpus": {
            "type": "object",
            "properties": {
              "coleccion": {
                "type": "string",
                "enum": [
                  "curado",
                  "archivo"
                ],
                "description": "`archivo`: la normativa del Archivo Soberano Luthor."
              },
              "corte": {
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Fecha de negocio (AAAA-MM-DD) del catálogo de la normativa (su censo), o `null`."
              },
              "revision": {
                "anyOf": [
                  {
                    "type": "string",
                    "maxLength": 128
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Identificador de la revisión del catálogo, o `null`."
              }
            },
            "required": [
              "coleccion",
              "corte",
              "revision"
            ],
            "additionalProperties": false,
            "description": "Qué colección sirvió la respuesta y de qué fecha es."
          }
        },
        "required": [
          "contrato",
          "resultados",
          "modo_efectivo",
          "truncado",
          "corpus"
        ],
        "additionalProperties": false
      },
      "luthor_leer_ordenamiento.entrada": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_leer_ordenamiento.entrada",
        "title": "Leer ordenamiento: entrada",
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^ORD-(?:0|[1-9][0-9]{0,11})(?:@(?:0|[1-9][0-9]{0,11}))?$",
            "description": "Identificador de la norma, p. ej. ORD-160795 (su versión vigente), o de una versión, p. ej. ORD-160795@3, tal como lo devuelve luthor_buscar_ordenamiento."
          },
          "articulo": {
            "type": "string",
            "description": "Número de un artículo de la norma, p. ej. 5, 5-A o PRIMERO. Va sin `consulta`.",
            "minLength": 1,
            "maxLength": 40,
            "pattern": "^[0-9A-Za-zÁÉÍÓÚÑáéíóúñ][0-9A-Za-zÁÉÍÓÚÑáéíóúñ .-]*$"
          },
          "consulta": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "pattern": "\\S",
            "description": "Tema a localizar dentro de la norma: entrega hasta 5 artículos que hablan de él, en orden de relevancia. Va sin `articulo`."
          },
          "fragmento": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "description": "Fragmento del documento, base 1. Los fragmentos son fijos por documento; cada uno es una lectura.",
            "default": 1
          }
        },
        "required": [
          "id"
        ],
        "additionalProperties": false,
        "description": "Pasa `articulo` o `consulta`, no ambos; sin ninguno, se lee todo el articulado por fragmentos."
      },
      "luthor_leer_ordenamiento.salida": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "urn:luthor:datos:v1:luthor_leer_ordenamiento.salida",
        "title": "Leer ordenamiento: resultado",
        "type": "object",
        "properties": {
          "contrato": {
            "const": "luthor-datos/1"
          },
          "documento": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^ORD-(?:0|[1-9][0-9]{0,11})@(?:0|[1-9][0-9]{0,11})$",
                "description": "La versión leída (`ORD-{id}@{rid}`): úsalo para pedir los demás fragmentos de la MISMA versión."
              },
              "familia": {
                "const": "ordenamiento",
                "description": "`ordenamiento`."
              },
              "url": {
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^https://luthor\\.mx/",
                    "maxLength": 256
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Página pública de la norma (o de la versión leída) en luthor.mx."
              },
              "procedencia": {
                "type": "object",
                "properties": {
                  "fuente": {
                    "const": "Archivo Soberano Luthor"
                  },
                  "revision": {
                    "anyOf": [
                      {
                        "type": "string",
                        "maxLength": 128
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "La versión leída (su `reforma_id`) o, en una búsqueda, `null`."
                  },
                  "fecha_revision": {
                    "anyOf": [
                      {
                        "type": "string",
                        "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Fecha de publicación de la versión leída, o `null`."
                  },
                  "ambito": {
                    "type": "string",
                    "enum": [
                      "federal",
                      "estatal",
                      "tratados"
                    ],
                    "description": "`federal`, `estatal` o `tratados` (internacionales)."
                  },
                  "entidad": {
                    "anyOf": [
                      {
                        "type": "string",
                        "enum": [
                          "aguascalientes",
                          "baja_california",
                          "baja_california_sur",
                          "campeche",
                          "chiapas",
                          "chihuahua",
                          "ciudad_de_mexico",
                          "coahuila",
                          "colima",
                          "durango",
                          "estado_de_mexico",
                          "guanajuato",
                          "guerrero",
                          "hidalgo",
                          "jalisco",
                          "michoacan",
                          "morelos",
                          "nayarit",
                          "nuevo_leon",
                          "oaxaca",
                          "puebla",
                          "queretaro",
                          "quintana_roo",
                          "san_luis_potosi",
                          "sinaloa",
                          "sonora",
                          "tabasco",
                          "tamaulipas",
                          "tlaxcala",
                          "veracruz",
                          "yucatan",
                          "zacatecas"
                        ]
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "La entidad federativa de una norma estatal; `null` en federal y tratados."
                  },
                  "categoria": {
                    "anyOf": [
                      {
                        "type": "string",
                        "maxLength": 64
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "La categoría tal como la trae el catálogo (LEY, REGLAMENTO, CODIGO…), o `null`."
                  }
                },
                "required": [
                  "fuente",
                  "revision",
                  "fecha_revision",
                  "ambito",
                  "entidad",
                  "categoria"
                ],
                "additionalProperties": false,
                "description": "De dónde sale la norma. La fuente es siempre «Archivo Soberano Luthor»."
              },
              "titulo": {
                "type": "string",
                "description": "Título de la norma, como lo trae el catálogo."
              },
              "vigencia_ordenamiento": {
                "description": "Qué se puede afirmar de la vigencia de la NORMA completa, con su fuente y su fecha de corte. No se refiere a un artículo.",
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "estado": {
                        "const": "vigente",
                        "description": "`vigente`, `no_vigente`, `abrogado`, `derogado`, `sin_efecto`, `indeterminado` (dos evidencias que chocan) o `desconocido`. «vigente» siempre con fuente y fecha de corte."
                      },
                      "fuente": {
                        "type": "string",
                        "enum": [
                          "catalogo_del_archivo",
                          "regla_ejercicio",
                          "abrogacion_con_cita"
                        ],
                        "description": "De dónde sale el estado: `catalogo_del_archivo` (la etiqueta del catálogo del Archivo Soberano Luthor a su fecha de corte), `regla_ejercicio` (una ley de ingresos o un presupuesto de un ejercicio concluido) o `abrogacion_con_cita` (una norma posterior que la abroga, citada)."
                      },
                      "fecha_corte": {
                        "type": "string",
                        "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$",
                        "description": "Fecha de negocio (AAAA-MM-DD) de la evidencia (hoy, el censo del catálogo); `null` si no la hay."
                      },
                      "fecha_efecto": {
                        "anyOf": [
                          {
                            "type": "string",
                            "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$",
                            "description": "Fecha de negocio (AAAA-MM-DD) en que el estado surtió efecto, o `null` si no se conoce."
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "confianza": {
                        "type": "string",
                        "enum": [
                          "alta",
                          "media",
                          "baja"
                        ],
                        "description": "`alta` = verificada; `media` o `baja` = registrada, no verificada."
                      },
                      "etiqueta_archivo": {
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 64,
                            "description": "La etiqueta de vigencia tal como la trae el catálogo, por transparencia, o `null`."
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "estado",
                      "fuente",
                      "fecha_corte",
                      "fecha_efecto",
                      "confianza",
                      "etiqueta_archivo"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "object",
                    "properties": {
                      "estado": {
                        "type": "string",
                        "enum": [
                          "no_vigente",
                          "abrogado",
                          "derogado",
                          "sin_efecto",
                          "indeterminado",
                          "desconocido"
                        ],
                        "description": "`vigente`, `no_vigente`, `abrogado`, `derogado`, `sin_efecto`, `indeterminado` (dos evidencias que chocan) o `desconocido`. «vigente» siempre con fuente y fecha de corte."
                      },
                      "fuente": {
                        "type": "string",
                        "enum": [
                          "catalogo_del_archivo",
                          "regla_ejercicio",
                          "abrogacion_con_cita"
                        ],
                        "description": "De dónde sale el estado: `catalogo_del_archivo` (la etiqueta del catálogo del Archivo Soberano Luthor a su fecha de corte), `regla_ejercicio` (una ley de ingresos o un presupuesto de un ejercicio concluido) o `abrogacion_con_cita` (una norma posterior que la abroga, citada)."
                      },
                      "fecha_corte": {
                        "anyOf": [
                          {
                            "type": "string",
                            "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "Fecha de negocio (AAAA-MM-DD) de la evidencia (hoy, el censo del catálogo); `null` si no la hay."
                      },
                      "fecha_efecto": {
                        "anyOf": [
                          {
                            "type": "string",
                            "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$",
                            "description": "Fecha de negocio (AAAA-MM-DD) en que el estado surtió efecto, o `null` si no se conoce."
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "confianza": {
                        "type": "string",
                        "enum": [
                          "alta",
                          "media",
                          "baja"
                        ],
                        "description": "`alta` = verificada; `media` o `baja` = registrada, no verificada."
                      },
                      "etiqueta_archivo": {
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 64,
                            "description": "La etiqueta de vigencia tal como la trae el catálogo, por transparencia, o `null`."
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "estado",
                      "fuente",
                      "fecha_corte",
                      "fecha_efecto",
                      "confianza",
                      "etiqueta_archivo"
                    ],
                    "additionalProperties": false
                  }
                ]
              },
              "version_vigente": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "reforma_id": {
                        "type": "string",
                        "pattern": "^(?:0|[1-9][0-9]{0,11})$",
                        "description": "Identificador de la versión (úsalo en `ORD-{id}@{reforma_id}`)."
                      },
                      "fecha_publicacion": {
                        "anyOf": [
                          {
                            "type": "string",
                            "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "Fecha de publicación de la versión (AAAA-MM-DD), o `null`."
                      },
                      "regla": {
                        "type": "string",
                        "enum": [
                          "censo",
                          "fecha_v1"
                        ],
                        "description": "Cómo se eligió: `censo` (el puntero del catálogo del archivo) o `fecha_v1` (la última publicada con fecha hasta hoy)."
                      },
                      "confianza": {
                        "type": "string",
                        "enum": [
                          "alta",
                          "media",
                          "baja"
                        ],
                        "description": "`alta` = verificada; `media` o `baja` = registrada, no verificada."
                      },
                      "texto": {
                        "type": "string",
                        "enum": [
                          "articulado",
                          "facsimil",
                          "sin_texto"
                        ],
                        "description": "Qué texto tiene esa versión: `articulado` (legible), `facsimil` (sólo imagen: no se puede leer) o `sin_texto`."
                      },
                      "ultima_con_articulado": {
                        "type": "object",
                        "properties": {
                          "reforma_id": {
                            "type": "string",
                            "pattern": "^(?:0|[1-9][0-9]{0,11})$",
                            "description": "Identificador de esa versión."
                          },
                          "fecha_publicacion": {
                            "anyOf": [
                              {
                                "type": "string",
                                "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                              },
                              {
                                "type": "null"
                              }
                            ],
                            "description": "Su fecha de publicación, o `null`."
                          }
                        },
                        "required": [
                          "reforma_id",
                          "fecha_publicacion"
                        ],
                        "additionalProperties": false,
                        "description": "Opcional: la última versión con articulado legible, cuando la vigente no lo tiene."
                      },
                      "proxima": {
                        "type": "object",
                        "properties": {
                          "reforma_id": {
                            "type": "string",
                            "pattern": "^(?:0|[1-9][0-9]{0,11})$",
                            "description": "Identificador de esa versión."
                          },
                          "entrada_en_vigor": {
                            "type": "string",
                            "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$",
                            "description": "Fecha en que entra en vigor (AAAA-MM-DD)."
                          }
                        },
                        "required": [
                          "reforma_id",
                          "entrada_en_vigor"
                        ],
                        "additionalProperties": false,
                        "description": "Opcional: una versión publicada que aún no entra en vigor."
                      },
                      "empate": {
                        "type": "array",
                        "minItems": 1,
                        "maxItems": 20,
                        "items": {
                          "type": "string",
                          "pattern": "^(?:0|[1-9][0-9]{0,11})$"
                        },
                        "description": "Opcional: otras versiones publicadas el mismo día que la vigente."
                      }
                    },
                    "required": [
                      "reforma_id",
                      "fecha_publicacion",
                      "regla",
                      "confianza",
                      "texto"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "La versión que rige hoy según el catálogo, con el texto que tiene; `null` si el catálogo no la determina."
              },
              "texto_transformado": {
                "type": "boolean"
              },
              "lectura": {
                "type": "object",
                "properties": {
                  "alcance": {
                    "type": "string",
                    "enum": [
                      "articulado",
                      "articulo",
                      "consulta"
                    ],
                    "description": "`articulado` (toda la norma), `articulo` (uno) o `consulta` (los que tratan el tema pedido)."
                  },
                  "articulos": {
                    "type": "array",
                    "maxItems": 5,
                    "description": "Los artículos entregados (vacío al leer el articulado completo).",
                    "items": {
                      "type": "object",
                      "properties": {
                        "numero": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 64,
                          "description": "Número del artículo, como lo trae la norma."
                        },
                        "url": {
                          "anyOf": [
                            {
                              "type": "string",
                              "pattern": "^https://luthor\\.mx/",
                              "maxLength": 256
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "Su URL en luthor.mx, o `null`."
                        }
                      },
                      "required": [
                        "numero",
                        "url"
                      ],
                      "additionalProperties": false
                    }
                  }
                },
                "required": [
                  "alcance",
                  "articulos"
                ],
                "additionalProperties": false,
                "description": "Qué entregó la lectura."
              },
              "texto": {
                "type": "string"
              },
              "truncado": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "truncado": {
                        "const": false
                      },
                      "motivo": {
                        "type": "null"
                      },
                      "fragmento": {
                        "const": 1
                      },
                      "fragmentos_totales": {
                        "const": 1
                      },
                      "bytes_entregados": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "bytes_totales": {
                        "type": "integer",
                        "minimum": 0
                      }
                    },
                    "required": [
                      "truncado",
                      "motivo",
                      "fragmento",
                      "fragmentos_totales",
                      "bytes_entregados",
                      "bytes_totales"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "object",
                    "properties": {
                      "truncado": {
                        "const": true
                      },
                      "motivo": {
                        "const": "FRAGMENTADO"
                      },
                      "fragmento": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "fragmentos_totales": {
                        "type": "integer",
                        "minimum": 2
                      },
                      "bytes_entregados": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "bytes_totales": {
                        "type": "integer",
                        "minimum": 0
                      }
                    },
                    "required": [
                      "truncado",
                      "motivo",
                      "fragmento",
                      "fragmentos_totales",
                      "bytes_entregados",
                      "bytes_totales"
                    ],
                    "additionalProperties": false
                  }
                ]
              }
            },
            "required": [
              "id",
              "familia",
              "url",
              "procedencia",
              "titulo",
              "vigencia_ordenamiento",
              "version_vigente",
              "texto_transformado",
              "lectura",
              "texto",
              "truncado"
            ],
            "additionalProperties": false,
            "description": "La norma leída, con su sobre."
          },
          "corpus": {
            "type": "object",
            "properties": {
              "coleccion": {
                "type": "string",
                "enum": [
                  "curado",
                  "archivo"
                ],
                "description": "`archivo`: la normativa del Archivo Soberano Luthor."
              },
              "corte": {
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Fecha de negocio (AAAA-MM-DD) del catálogo de la normativa (su censo), o `null`."
              },
              "revision": {
                "anyOf": [
                  {
                    "type": "string",
                    "maxLength": 128
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Identificador de la revisión del catálogo, o `null`."
              }
            },
            "required": [
              "coleccion",
              "corte",
              "revision"
            ],
            "additionalProperties": false,
            "description": "Qué colección sirvió la respuesta y de qué fecha es."
          }
        },
        "required": [
          "contrato",
          "documento",
          "corpus"
        ],
        "additionalProperties": false
      }
    }
  }
}