{
  "openapi": "3.1.1",
  "info": {
    "title": "DatIACode Public API",
    "version": "1.0.0",
    "summary": "Catálogo de DatIACode en JSON: cursos, convocatorias de Academia, servicios y artículos.",
    "description": "API pública de **solo lectura**. No requiere autenticación, admite CORS desde cualquier origen y responde a `GET`, `HEAD` y `OPTIONS`.\n\nLas colecciones devuelven el sobre `{ data, meta, links }` con paginación por `page`/`pageSize`, y los recursos individuales `{ data }`. Todas las URL que aparecen en las respuestas son absolutas.\n\n`/cohorts` es el único recurso con datos vivos: precio, plazas disponibles y estado de la convocatoria cambian sin que haya un despliegue, y se sirven con una caché de 300 segundos. El resto del catálogo cambia con el código y se cachea una hora.\n\nLos formularios de contacto, las descargas y el pago **no** forman parte de esta API: viven en la web, con verificación anti-abuso, y no se exponen para automatización.",
    "termsOfService": "https://www.datiacode.com/legal/terminos",
    "contact": {
      "name": "DatIACode",
      "url": "https://www.datiacode.com/contacto",
      "email": "info@datiacode.com"
    }
  },
  "servers": [
    {
      "url": "https://www.datiacode.com",
      "description": "Producción"
    }
  ],
  "externalDocs": {
    "description": "Contexto de la empresa para agentes (llms.txt)",
    "url": "https://www.datiacode.com/llms.txt"
  },
  "tags": [
    {
      "name": "meta",
      "description": "Raíz de la API y descubrimiento."
    },
    {
      "name": "courses",
      "description": "Catálogo de formación."
    },
    {
      "name": "cohorts",
      "description": "Convocatorias de Academia con datos vivos."
    },
    {
      "name": "services",
      "description": "Servicios de consultoría, desarrollo y formación."
    },
    {
      "name": "articles",
      "description": "Blog técnico."
    }
  ],
  "security": [],
  "paths": {
    "/api/public/v1": {
      "get": {
        "tags": [
          "meta"
        ],
        "summary": "Raíz de la API pública",
        "description": "Describe la API, sus endpoints y los documentos de descubrimiento asociados.",
        "operationId": "getApiRoot",
        "responses": {
          "200": {
            "description": "Descripción de la API.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Demasiadas peticiones en poco tiempo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "Error inesperado del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "description": "Dependencia no disponible: el catálogo de convocatorias no ha podido leerse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/courses": {
      "get": {
        "tags": [
          "courses"
        ],
        "summary": "Catálogo de cursos",
        "description": "Cursos publicados del catálogo de formación. Los borradores no se exponen.",
        "operationId": "listCourses",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Página solicitada, empezando en 1.",
            "schema": {
              "default": 1,
              "type": "integer",
              "minimum": 1,
              "maximum": 10000
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Elementos por página (máximo 100).",
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda libre. Todos los términos deben aparecer en el título, el resumen o las etiquetas; ignora tildes y mayúsculas.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 80
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "inteligencia-artificial",
                "big-data",
                "cloud-computing",
                "programacion",
                "analisis-de-datos",
                "executive-tech"
              ]
            }
          },
          {
            "name": "subcategory",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ia-generativa-productividad",
                "rag-agentes",
                "ia-desarrollo-software",
                "machine-learning-deep-learning",
                "evaluacion-gobierno-ia",
                "java-spring",
                "python",
                "desarrollo-web-fullstack",
                "arquitectura-calidad",
                "sql-bases-datos"
              ]
            }
          },
          {
            "name": "level",
            "in": "query",
            "required": false,
            "description": "Nivel del curso.",
            "schema": {
              "type": "string",
              "enum": [
                "iniciacion",
                "intermedio",
                "avanzado"
              ]
            }
          },
          {
            "name": "modality",
            "in": "query",
            "required": false,
            "description": "Modalidad en la que se imparte.",
            "schema": {
              "type": "string",
              "enum": [
                "online",
                "presencial",
                "in-company",
                "a-medida"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Orden. El prefijo `-` invierte el sentido.",
            "schema": {
              "default": "title",
              "type": "string",
              "enum": [
                "title",
                "-title",
                "updated",
                "-updated"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Colección paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta",
                    "links"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CourseSummary"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/CollectionMeta"
                    },
                    "links": {
                      "$ref": "#/components/schemas/CollectionLinks"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Parámetros de consulta desconocidos o con valores no válidos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Demasiadas peticiones en poco tiempo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "Error inesperado del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "description": "Dependencia no disponible: el catálogo de convocatorias no ha podido leerse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/courses/{slug}": {
      "get": {
        "tags": [
          "courses"
        ],
        "summary": "Ficha de un curso",
        "description": "Temario, objetivos, perfiles, requisitos y preguntas frecuentes de un curso del catálogo.",
        "operationId": "getCourse",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Identificador del curso, tal como aparece en su URL pública.",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recurso encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Course"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Demasiadas peticiones en poco tiempo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "Error inesperado del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "description": "Dependencia no disponible: el catálogo de convocatorias no ha podido leerse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/cohorts": {
      "get": {
        "tags": [
          "cohorts"
        ],
        "summary": "Convocatorias de Academia",
        "description": "Convocatorias publicadas con fechas, horario, precio, plazas disponibles y profesorado. Filtra por `status=open` para quedarte solo con las que admiten reserva.",
        "operationId": "listCohorts",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Página solicitada, empezando en 1.",
            "schema": {
              "default": 1,
              "type": "integer",
              "minimum": 1,
              "maximum": 10000
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Elementos por página (máximo 100).",
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda libre. Todos los términos deben aparecer en el título, el resumen o las etiquetas; ignora tildes y mayúsculas.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 80
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Estado de la convocatoria. `open` es la única que admite reserva.",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "sold_out",
                "started",
                "finished",
                "closed"
              ]
            }
          },
          {
            "name": "courseSlug",
            "in": "query",
            "required": false,
            "description": "Convocatorias de un curso concreto de Academia.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 120
            }
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "description": "Modalidad de impartición.",
            "schema": {
              "type": "string",
              "enum": [
                "online",
                "presencial"
              ]
            }
          },
          {
            "name": "startsAfter",
            "in": "query",
            "required": false,
            "description": "Solo las que empiezan en esta fecha o después (ISO 8601).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "startsBefore",
            "in": "query",
            "required": false,
            "description": "Solo las que empiezan en esta fecha o antes (ISO 8601).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "maxPrice",
            "in": "query",
            "required": false,
            "description": "Precio máximo por participante, en la moneda de la convocatoria.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 1000000
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Orden. El prefijo `-` invierte el sentido.",
            "schema": {
              "default": "startDate",
              "type": "string",
              "enum": [
                "startDate",
                "-startDate",
                "price",
                "-price"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Colección paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta",
                    "links"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CohortSummary"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/CollectionMeta"
                    },
                    "links": {
                      "$ref": "#/components/schemas/CollectionLinks"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Parámetros de consulta desconocidos o con valores no válidos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Demasiadas peticiones en poco tiempo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "Error inesperado del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "description": "Dependencia no disponible: el catálogo de convocatorias no ha podido leerse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/cohorts/{slug}": {
      "get": {
        "tags": [
          "cohorts"
        ],
        "summary": "Convocatoria concreta",
        "description": "Convocatoria con el calendario completo de sesiones, la descripción de la ficha y sus preguntas frecuentes.",
        "operationId": "getCohort",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Identificador de la convocatoria.",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recurso encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Cohort"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Demasiadas peticiones en poco tiempo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "Error inesperado del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "description": "Dependencia no disponible: el catálogo de convocatorias no ha podido leerse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/services": {
      "get": {
        "tags": [
          "services"
        ],
        "summary": "Catálogo de servicios",
        "description": "Servicios que presta DatIACode.",
        "operationId": "listServices",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Página solicitada, empezando en 1.",
            "schema": {
              "default": 1,
              "type": "integer",
              "minimum": 1,
              "maximum": 10000
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Elementos por página (máximo 100).",
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda libre. Todos los términos deben aparecer en el título, el resumen o las etiquetas; ignora tildes y mayúsculas.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 80
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Familia de servicio.",
            "schema": {
              "type": "string",
              "enum": [
                "Formación",
                "Consultoría",
                "Desarrollo",
                "Contenidos"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Orden. El prefijo `-` invierte el sentido.",
            "schema": {
              "default": "title",
              "type": "string",
              "enum": [
                "title",
                "-title"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Colección paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta",
                    "links"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ServiceSummary"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/CollectionMeta"
                    },
                    "links": {
                      "$ref": "#/components/schemas/CollectionLinks"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Parámetros de consulta desconocidos o con valores no válidos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Demasiadas peticiones en poco tiempo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "Error inesperado del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "description": "Dependencia no disponible: el catálogo de convocatorias no ha podido leerse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/services/{slug}": {
      "get": {
        "tags": [
          "services"
        ],
        "summary": "Ficha de un servicio",
        "description": "Alcance, proceso, casos de uso y preguntas frecuentes de un servicio.",
        "operationId": "getService",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Identificador del servicio.",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recurso encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Service"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Demasiadas peticiones en poco tiempo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "Error inesperado del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "description": "Dependencia no disponible: el catálogo de convocatorias no ha podido leerse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/articles": {
      "get": {
        "tags": [
          "articles"
        ],
        "summary": "Índice del blog",
        "description": "Artículos publicados, del más reciente al más antiguo.",
        "operationId": "listArticles",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Página solicitada, empezando en 1.",
            "schema": {
              "default": 1,
              "type": "integer",
              "minimum": 1,
              "maximum": 10000
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Elementos por página (máximo 100).",
            "schema": {
              "default": 50,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Búsqueda libre. Todos los términos deben aparecer en el título, el resumen o las etiquetas; ignora tildes y mayúsculas.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 80
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Categoría editorial exacta, p. ej. «Desarrollo con IA».",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 80
            }
          },
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "description": "Etiqueta del artículo (coincidencia parcial).",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 80
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Orden. Por defecto, lo más reciente primero.",
            "schema": {
              "default": "-publishedAt",
              "type": "string",
              "enum": [
                "publishedAt",
                "-publishedAt",
                "title",
                "-title"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Colección paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta",
                    "links"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArticleSummary"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/CollectionMeta"
                    },
                    "links": {
                      "$ref": "#/components/schemas/CollectionLinks"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Parámetros de consulta desconocidos o con valores no válidos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Demasiadas peticiones en poco tiempo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "Error inesperado del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "description": "Dependencia no disponible: el catálogo de convocatorias no ha podido leerse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/articles/{slug}": {
      "get": {
        "tags": [
          "articles"
        ],
        "summary": "Artículo completo",
        "description": "Artículo con su contenido estructurado: secciones con su ancla estable, tablas, código, fuentes y FAQ.",
        "operationId": "getArticle",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Identificador del artículo.",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recurso encontrado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Article"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "El recurso no existe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Demasiadas peticiones en poco tiempo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "Error inesperado del servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "503": {
            "description": "Dependencia no disponible: el catálogo de convocatorias no ha podido leerse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CourseSummary": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "title": {
            "type": "string"
          },
          "excerpt": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "category": {
            "$ref": "#/components/schemas/TaxonomyRef"
          },
          "subcategory": {
            "$ref": "#/components/schemas/TaxonomyRef"
          },
          "level": {
            "type": "string"
          },
          "modalities": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "duration": {
            "type": "string"
          },
          "audience": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "image": {
            "type": "string",
            "format": "uri"
          },
          "featured": {
            "type": "boolean"
          },
          "updated": {
            "type": "string",
            "minLength": 8
          }
        },
        "required": [
          "slug",
          "title",
          "excerpt",
          "url",
          "category",
          "level",
          "modalities",
          "duration",
          "audience",
          "tags",
          "updated"
        ],
        "additionalProperties": false,
        "id": "CourseSummary",
        "description": "Curso del catálogo de formación en su forma reducida, la que devuelve la colección."
      },
      "TaxonomyRef": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "slug",
          "name",
          "url"
        ],
        "additionalProperties": false,
        "id": "TaxonomyRef",
        "description": "Categoría o subcategoría del catálogo."
      },
      "Course": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "title": {
            "type": "string"
          },
          "excerpt": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "category": {
            "$ref": "#/components/schemas/TaxonomyRef"
          },
          "subcategory": {
            "$ref": "#/components/schemas/TaxonomyRef"
          },
          "level": {
            "type": "string"
          },
          "modalities": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "duration": {
            "type": "string"
          },
          "audience": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "image": {
            "type": "string",
            "format": "uri"
          },
          "featured": {
            "type": "boolean"
          },
          "updated": {
            "type": "string",
            "minLength": 8
          },
          "introduction": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "whatYouLearn": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "objectives": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "syllabus": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SyllabusModule"
            }
          },
          "audiences": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CourseAudience"
            }
          },
          "applications": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "requirements": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "faqs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Faq"
            }
          },
          "relatedCourses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResourceRef"
            }
          }
        },
        "required": [
          "slug",
          "title",
          "excerpt",
          "url",
          "category",
          "level",
          "modalities",
          "duration",
          "audience",
          "tags",
          "updated",
          "introduction",
          "whatYouLearn",
          "objectives",
          "syllabus",
          "audiences",
          "applications",
          "requirements",
          "faqs",
          "relatedCourses"
        ],
        "additionalProperties": false,
        "id": "Course",
        "description": "Curso del catálogo con el detalle completo: temario, objetivos, requisitos y FAQ."
      },
      "SyllabusModule": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "bullets": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "title",
          "bullets"
        ],
        "additionalProperties": false,
        "id": "SyllabusModule",
        "description": "Módulo del temario."
      },
      "CourseAudience": {
        "type": "object",
        "properties": {
          "profile": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        },
        "required": [
          "profile",
          "description"
        ],
        "additionalProperties": false,
        "id": "CourseAudience",
        "description": "Perfil al que se dirige el curso."
      },
      "Faq": {
        "type": "object",
        "properties": {
          "question": {
            "type": "string"
          },
          "answer": {
            "type": "string"
          }
        },
        "required": [
          "question",
          "answer"
        ],
        "additionalProperties": false,
        "id": "Faq",
        "description": "Pregunta frecuente publicada en la web."
      },
      "ResourceRef": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "slug",
          "title",
          "url"
        ],
        "additionalProperties": false,
        "id": "ResourceRef",
        "description": "Referencia a otro recurso público."
      },
      "CohortSummary": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "courseSlug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "courseTitle": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "editionLabel": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/CohortStatus"
          },
          "availability": {
            "type": "string"
          },
          "checkoutAvailable": {
            "type": "boolean"
          },
          "startDate": {
            "type": "string",
            "minLength": 8
          },
          "endDate": {
            "type": "string",
            "minLength": 8
          },
          "timezone": {
            "type": "string"
          },
          "durationHours": {
            "type": "number"
          },
          "schedule": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "level": {
            "type": "string"
          },
          "price": {
            "$ref": "#/components/schemas/Price"
          },
          "capacity": {
            "$ref": "#/components/schemas/Capacity"
          },
          "instructors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CohortInstructor"
            }
          }
        },
        "required": [
          "slug",
          "courseSlug",
          "courseTitle",
          "summary",
          "url",
          "status",
          "checkoutAvailable",
          "startDate",
          "endDate",
          "timezone",
          "durationHours",
          "mode",
          "language",
          "level",
          "price",
          "capacity",
          "instructors"
        ],
        "additionalProperties": false,
        "id": "CohortSummary",
        "description": "Convocatoria de Academia en su forma reducida."
      },
      "CohortStatus": {
        "type": "string",
        "enum": [
          "open",
          "sold_out",
          "started",
          "finished",
          "closed"
        ],
        "id": "CohortStatus",
        "description": "open: admite reserva. sold_out: aforo completo. started: ya ha empezado. finished: terminada. closed: convocatoria cerrada."
      },
      "Price": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "number"
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3
          },
          "listAmount": {
            "type": "number"
          }
        },
        "required": [
          "amount",
          "currency"
        ],
        "additionalProperties": false,
        "id": "Price",
        "description": "Precio por participante. `amount` es el precio vigente y `listAmount` el precio normal cuando hay lanzamiento."
      },
      "Capacity": {
        "type": "object",
        "properties": {
          "maxSeats": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "seatsAvailable": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "maxSeats",
          "seatsAvailable"
        ],
        "additionalProperties": false,
        "id": "Capacity",
        "description": "Aforo de la convocatoria y plazas libres."
      },
      "CohortInstructor": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "bio": {
            "type": "string"
          },
          "photo": {
            "type": "string",
            "format": "uri"
          },
          "linkedin": {
            "type": "string",
            "format": "uri"
          },
          "website": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "id": "CohortInstructor",
        "description": "Docente de la convocatoria."
      },
      "Cohort": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "courseSlug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "courseTitle": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "editionLabel": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/CohortStatus"
          },
          "availability": {
            "type": "string"
          },
          "checkoutAvailable": {
            "type": "boolean"
          },
          "startDate": {
            "type": "string",
            "minLength": 8
          },
          "endDate": {
            "type": "string",
            "minLength": 8
          },
          "timezone": {
            "type": "string"
          },
          "durationHours": {
            "type": "number"
          },
          "schedule": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "level": {
            "type": "string"
          },
          "price": {
            "$ref": "#/components/schemas/Price"
          },
          "capacity": {
            "$ref": "#/components/schemas/Capacity"
          },
          "instructors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CohortInstructor"
            }
          },
          "sessions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Session"
            }
          },
          "description": {
            "type": "string"
          },
          "faqs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Faq"
            }
          }
        },
        "required": [
          "slug",
          "courseSlug",
          "courseTitle",
          "summary",
          "url",
          "status",
          "checkoutAvailable",
          "startDate",
          "endDate",
          "timezone",
          "durationHours",
          "mode",
          "language",
          "level",
          "price",
          "capacity",
          "instructors",
          "sessions",
          "faqs"
        ],
        "additionalProperties": false,
        "id": "Cohort",
        "description": "Convocatoria de Academia con el calendario de sesiones y la descripción de la ficha."
      },
      "Session": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string"
          },
          "start": {
            "type": "string"
          },
          "end": {
            "type": "string"
          }
        },
        "required": [
          "date",
          "start",
          "end"
        ],
        "additionalProperties": false,
        "id": "Session",
        "description": "Sesión en directo: fecha y horas locales."
      },
      "ServiceSummary": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "title": {
            "type": "string"
          },
          "shortDescription": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "category": {
            "type": "string"
          },
          "badges": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "image": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "slug",
          "title",
          "shortDescription",
          "url",
          "category",
          "badges",
          "tags"
        ],
        "additionalProperties": false,
        "id": "ServiceSummary",
        "description": "Servicio en su forma reducida."
      },
      "Service": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "title": {
            "type": "string"
          },
          "shortDescription": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "category": {
            "type": "string"
          },
          "badges": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "image": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": "string"
          },
          "introduction": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "problems": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "benefits": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "includes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "process": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProcessStep"
            }
          },
          "useCases": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "audiences": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "faqs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Faq"
            }
          },
          "relatedServices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResourceRef"
            }
          }
        },
        "required": [
          "slug",
          "title",
          "shortDescription",
          "url",
          "category",
          "badges",
          "tags",
          "description",
          "introduction",
          "problems",
          "benefits",
          "includes",
          "process",
          "useCases",
          "audiences",
          "faqs",
          "relatedServices"
        ],
        "additionalProperties": false,
        "id": "Service",
        "description": "Servicio con el detalle completo que publica su ficha."
      },
      "ProcessStep": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        },
        "required": [
          "title",
          "description"
        ],
        "additionalProperties": false,
        "id": "ProcessStep",
        "description": "Fase del proceso de trabajo."
      },
      "ArticleSummary": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "title": {
            "type": "string"
          },
          "excerpt": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "category": {
            "type": "string"
          },
          "publishedAt": {
            "type": "string",
            "minLength": 8
          },
          "updatedAt": {
            "type": "string",
            "minLength": 8
          },
          "author": {
            "type": "string"
          },
          "readingTime": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "image": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "slug",
          "title",
          "excerpt",
          "url",
          "category",
          "publishedAt",
          "updatedAt",
          "author",
          "readingTime",
          "tags"
        ],
        "additionalProperties": false,
        "id": "ArticleSummary",
        "description": "Artículo del blog en su forma reducida."
      },
      "Article": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "title": {
            "type": "string"
          },
          "excerpt": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "category": {
            "type": "string"
          },
          "publishedAt": {
            "type": "string",
            "minLength": 8
          },
          "updatedAt": {
            "type": "string",
            "minLength": 8
          },
          "author": {
            "type": "string"
          },
          "readingTime": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "image": {
            "type": "string",
            "format": "uri"
          },
          "content": {
            "type": "object",
            "properties": {
              "intro": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "sections": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ArticleSection"
                }
              },
              "conclusion": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "sources": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ArticleSource"
                }
              },
              "faqs": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Faq"
                }
              }
            },
            "required": [
              "intro",
              "sections",
              "conclusion"
            ],
            "additionalProperties": false
          },
          "related": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResourceRef"
            }
          }
        },
        "required": [
          "slug",
          "title",
          "excerpt",
          "url",
          "category",
          "publishedAt",
          "updatedAt",
          "author",
          "readingTime",
          "tags",
          "content",
          "related"
        ],
        "additionalProperties": false,
        "id": "Article",
        "description": "Artículo del blog con su contenido estructurado, sección a sección."
      },
      "ArticleSection": {
        "type": "object",
        "properties": {
          "heading": {
            "type": "string"
          },
          "anchor": {
            "type": "string"
          },
          "body": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "bullets": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "table": {
            "$ref": "#/components/schemas/ArticleTable"
          },
          "code": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArticleCode"
            }
          },
          "callout": {
            "$ref": "#/components/schemas/ArticleCallout"
          },
          "image": {
            "$ref": "#/components/schemas/ArticleImage"
          },
          "stats": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArticleStat"
            }
          }
        },
        "required": [
          "heading",
          "anchor",
          "body"
        ],
        "additionalProperties": false,
        "id": "ArticleSection",
        "description": "Sección del artículo, con su ancla estable."
      },
      "ArticleTable": {
        "type": "object",
        "properties": {
          "caption": {
            "type": "string"
          },
          "headers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "note": {
            "type": "string"
          }
        },
        "required": [
          "headers",
          "rows"
        ],
        "additionalProperties": false,
        "id": "ArticleTable",
        "description": "Tabla comparativa del artículo."
      },
      "ArticleCode": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "code": {
            "type": "string"
          }
        },
        "required": [
          "code"
        ],
        "additionalProperties": false,
        "id": "ArticleCode",
        "description": "Bloque de código del artículo."
      },
      "ArticleCallout": {
        "type": "object",
        "properties": {
          "variant": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": "string"
          }
        },
        "required": [
          "variant",
          "body"
        ],
        "additionalProperties": false,
        "id": "ArticleCallout",
        "description": "Aviso destacado del artículo."
      },
      "ArticleImage": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "alt": {
            "type": "string"
          },
          "caption": {
            "type": "string"
          }
        },
        "required": [
          "url",
          "alt"
        ],
        "additionalProperties": false,
        "id": "ArticleImage",
        "description": "Ilustración del artículo."
      },
      "ArticleStat": {
        "type": "object",
        "properties": {
          "value": {
            "type": "string"
          },
          "label": {
            "type": "string"
          }
        },
        "required": [
          "value",
          "label"
        ],
        "additionalProperties": false,
        "id": "ArticleStat",
        "description": "Cifra destacada del artículo."
      },
      "ArticleSource": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "title",
          "url"
        ],
        "additionalProperties": false,
        "id": "ArticleSource",
        "description": "Documentación primaria citada."
      },
      "CollectionMeta": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "pageSize": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "totalItems": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "totalPages": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "page",
          "pageSize",
          "totalItems",
          "totalPages"
        ],
        "additionalProperties": false,
        "id": "CollectionMeta",
        "description": "Metadatos de paginación."
      },
      "CollectionLinks": {
        "type": "object",
        "properties": {
          "self": {
            "type": "string",
            "format": "uri"
          },
          "next": {
            "anyOf": [
              {
                "type": "string",
                "format": "uri"
              },
              {
                "type": "null"
              }
            ]
          },
          "prev": {
            "anyOf": [
              {
                "type": "string",
                "format": "uri"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "self",
          "next",
          "prev"
        ],
        "additionalProperties": false,
        "id": "CollectionLinks",
        "description": "Enlaces de navegación de la colección."
      },
      "ApiError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "field",
                    "message"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false,
        "id": "ApiError",
        "description": "Error de la API pública."
      }
    }
  }
}