{
  "openapi": "3.1.0",
  "info": {
    "title": "DistriCort Paraguay — API pública para agentes",
    "description": "Cotización agéntica de cortinas en Paraguay (PYG). Sin autenticación. Reglas de uso y rate-limits en https://www.districort.com.py/agent-terms",
    "version": "1.0.0",
    "termsOfService": "https://www.districort.com.py/agent-terms",
    "contact": {
      "name": "DistriCort Paraguay — API para agentes",
      "url": "https://www.districort.com.py/agent-terms",
      "email": "info@districort.com.py"
    }
  },
  "servers": [
    {
      "url": "https://www.districort.com.py",
      "description": "Servidor de Producción DistriCort"
    }
  ],
  "paths": {
    "/api/public/agent/catalogo": {
      "get": {
        "summary": "Catálogo de tipos, tejidos y espacios",
        "description": "Retorna el catálogo público de modelos, reglas de validación y telas permitidas por modelo.",
        "operationId": "getCatalogoAgente",
        "parameters": [
          {
            "name": "X-Agent-Name",
            "in": "header",
            "required": false,
            "description": "Opcional. Nombre de tu agente para trazabilidad y límites justos. No es autenticación. Ver /agent-terms §7.",
            "schema": { "type": "string", "example": "MiAsistente/1.2" }
          }
        ],
        "responses": {
          "200": {
            "description": "Catálogo disponible",
            "headers": {
              "RateLimit-Policy": { "description": "Política de límite aplicada (cuota `q` en ventana `w` segundos).", "schema": { "type": "string", "example": "\"default\";q=60;w=60" } },
              "RateLimit-Limit": { "description": "Solicitudes permitidas en la ventana actual.", "schema": { "type": "integer", "example": 60 } },
              "RateLimit-Remaining": { "description": "Solicitudes restantes en la ventana actual.", "schema": { "type": "integer", "example": 57 } },
              "RateLimit-Reset": { "description": "Segundos hasta el reseteo de la ventana.", "schema": { "type": "integer", "example": 38 } }
            },
            "content": {
              "application/json": { "schema": { "type": "object" } }
            }
          },
          "429": {
            "description": "Límite excedido. Respetá Retry-After.",
            "headers": {
              "Retry-After": { "description": "Segundos a esperar antes de reintentar.", "schema": { "type": "integer", "example": 38 } },
              "RateLimit-Remaining": { "description": "Solicitudes restantes en la ventana actual.", "schema": { "type": "integer", "example": 0 } },
              "RateLimit-Reset": { "description": "Segundos hasta el reseteo de la ventana.", "schema": { "type": "integer", "example": 38 } }
            },
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/RateLimitError" }
              }
            }
          }
        }
      }
    },
    "/api/public/agent/presupuestos": {
      "post": {
        "summary": "Generar cotización multi-cortina en PYG",
        "description": "Calcula el presupuesto consolidado para una o múltiples cortinas en Guaraníes (PYG) y emite el documento PDF oficial.",
        "operationId": "generarPresupuestoOficial",
        "parameters": [
          {
            "name": "X-Agent-Name",
            "in": "header",
            "required": false,
            "description": "Opcional. Nombre de tu agente para trazabilidad y límites justos. No es autenticación. Ver /agent-terms §7.",
            "schema": { "type": "string", "example": "MiAsistente/1.2" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/PresupuestoRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Presupuesto generado exitosamente",
            "headers": {
              "RateLimit-Policy": { "description": "Política de límite aplicada (cuota `q` en ventana `w` segundos).", "schema": { "type": "string", "example": "\"default\";q=20;w=60" } },
              "RateLimit-Limit": { "description": "Solicitudes permitidas en la ventana actual.", "schema": { "type": "integer", "example": 20 } },
              "RateLimit-Remaining": { "description": "Solicitudes restantes en la ventana actual.", "schema": { "type": "integer", "example": 17 } },
              "RateLimit-Reset": { "description": "Segundos hasta el reseteo de la ventana.", "schema": { "type": "integer", "example": 38 } }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PresupuestoResponse" }
              }
            }
          },
          "422": {
            "description": "Payload inválido (p. ej. dimensiones fuera de rango)."
          },
          "429": {
            "description": "Límite excedido. Respetá Retry-After.",
            "headers": {
              "Retry-After": { "description": "Segundos a esperar antes de reintentar.", "schema": { "type": "integer", "example": 38 } },
              "RateLimit-Remaining": { "description": "Solicitudes restantes en la ventana actual.", "schema": { "type": "integer", "example": 0 } },
              "RateLimit-Reset": { "description": "Segundos hasta el reseteo de la ventana.", "schema": { "type": "integer", "example": 38 } }
            },
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/RateLimitError" }
              }
            }
          }
        }
      }
    },
    "/api/public/agent/presupuestos/{id}/pdf": {
      "get": {
        "summary": "Descargar PDF oficial del presupuesto",
        "description": "Retorna el documento PDF oficial generado por el servidor listo para visualizar o descargar.",
        "operationId": "descargarPdfPresupuesto",
        "parameters": [
          {
            "name": "X-Agent-Name",
            "in": "header",
            "required": false,
            "description": "Opcional. Nombre de tu agente para trazabilidad y límites justos. No es autenticación. Ver /agent-terms §7.",
            "schema": { "type": "string", "example": "MiAsistente/1.2" }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "example": "DC-2026-00482" },
            "description": "UUID del presupuesto generado (idPresupuesto)"
          }
        ],
        "responses": {
          "200": {
            "description": "Documento PDF del presupuesto oficial",
            "headers": {
              "RateLimit-Limit": { "description": "Solicitudes permitidas en la ventana actual.", "schema": { "type": "integer", "example": 20 } },
              "RateLimit-Remaining": { "description": "Solicitudes restantes en la ventana actual.", "schema": { "type": "integer", "example": 17 } },
              "RateLimit-Reset": { "description": "Segundos hasta el reseteo de la ventana.", "schema": { "type": "integer", "example": 38 } }
            },
            "content": {
              "application/pdf": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          },
          "404": {
            "description": "Presupuesto inexistente o expirado (>7 días)."
          },
          "429": {
            "description": "Límite excedido. Respetá Retry-After.",
            "headers": {
              "Retry-After": { "description": "Segundos a esperar antes de reintentar.", "schema": { "type": "integer", "example": 38 } },
              "RateLimit-Reset": { "description": "Segundos hasta el reseteo de la ventana.", "schema": { "type": "integer", "example": 38 } }
            },
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/RateLimitError" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "XAgentName": {
        "name": "X-Agent-Name",
        "in": "header",
        "required": false,
        "description": "Opcional. Nombre de tu agente para trazabilidad y límites justos. No es autenticación. Ver /agent-terms §7.",
        "schema": { "type": "string", "example": "MiAsistente/1.2" }
      }
    },
    "headers": {
      "RateLimitPolicy": {
        "description": "Política de límite aplicada (cuota `q` en ventana `w` segundos).",
        "schema": { "type": "string", "example": "\"default\";q=20;w=60" }
      },
      "RateLimitLimit": {
        "description": "Solicitudes permitidas en la ventana actual.",
        "schema": { "type": "integer", "example": 20 }
      },
      "RateLimitRemaining": {
        "description": "Solicitudes restantes en la ventana actual.",
        "schema": { "type": "integer", "example": 17 }
      },
      "RateLimitReset": {
        "description": "Segundos hasta el reseteo de la ventana.",
        "schema": { "type": "integer", "example": 38 }
      },
      "RetryAfter": {
        "description": "Segundos a esperar antes de reintentar (solo en 429).",
        "schema": { "type": "integer", "example": 38 }
      }
    },
    "schemas": {
      "RateLimitError": {
        "type": "object",
        "required": ["error", "message", "retry_after_seconds"],
        "properties": {
          "error": { "type": "string", "example": "rate_limit_exceeded" },
          "title": { "type": "string", "example": "Too Many Requests" },
          "status": { "type": "integer", "example": 429 },
          "message": { "type": "string", "example": "Superaste 20 req/min en /presupuestos. Reintentá en 38s." },
          "retry_after_seconds": { "type": "integer", "example": 38 },
          "docs": { "type": "string", "example": "https://www.districort.com.py/agent-terms#8-límites-de-uso-rate-limits" }
        }
      },
      "PresupuestoRequest": {
        "type": "object",
        "required": ["clientName", "clientCity", "items"],
        "properties": {
          "clientName": { "type": "string", "example": "Juan Pérez" },
          "clientCity": { "type": "string", "example": "San Bernardino" },
          "items": {
            "type": "array",
            "minItems": 1,
            "items": { "$ref": "#/components/schemas/ItemCortinaRequest" }
          }
        }
      },
      "ItemCortinaRequest": {
        "type": "object",
        "required": ["idTipoCortina", "idEspacio", "ancho", "largo"],
        "properties": {
          "idTipoCortina": { "type": "integer", "example": 1, "description": "ID del tipo de cortina: 1=Roller Antisolar, 2=Roller Sunscreen, 16=Doble Visión Antisolar, 6=Horizontal Aluminio, etc." },
          "idTejido": { "type": ["integer", "null"], "nullable": true, "example": 2, "description": "ID de la tela/tejido. Enviar null para 'A definir (Muestrario a domicilio)'" },
          "idEspacio": { "type": "integer", "example": 2, "description": "ID del ambiente/espacio: 2=Sala de Estar, 4=Dormitorio Principal, 9=Cocina, 12=Oficina, etc." },
          "ancho": { "type": "number", "example": 1.80, "description": "Ancho en METROS decimales (ej: 1.80 para 1.80m)" },
          "largo": { "type": "number", "example": 2.20, "description": "Alto o largo en METROS decimales (ej: 2.20 para 2.20m)" },
          "cantidad": { "type": "integer", "default": 1, "example": 2 }
        }
      },
      "PresupuestoResponse": {
        "type": "object",
        "properties": {
          "idPresupuesto": { "type": "string", "example": "324dccc2-5ac1-4b05-ab05-95add6d2d57e", "description": "UUID del presupuesto generado" },
          "montoTotal": { "type": "number", "example": 4459200, "description": "Monto total en Guaraníes (PYG)" },
          "montoFinalSugerido": { "type": "number", "example": 3490000, "description": "Monto final con descuento sugerido en Guaraníes (PYG)" },
          "moneda": { "type": "string", "example": "PYG", "description": "Moneda usada (Guaraníes)" },
          "validezHasta": { "type": "string", "format": "date", "example": "2026-07-27", "description": "7 días desde la generación. Ver /agent-terms §3." },
          "pdfUrl": { "type": "string", "example": "https://www.districort.com.py/api/public/agent/presupuestos/324dccc2-5ac1-4b05-ab05-95add6d2d57e/pdf", "description": "URL directa de descarga del documento PDF oficial emitido. MOSTRAR ESTE ENLACE AL CLIENTE EN EL CHAT." },
          "googleLoginUrl": { "type": "string", "description": "URL para agendar visita a domicilio vinculando con Google SSO" },
          "agendarWhatsApp": { "type": "string", "format": "uri", "description": "Link wa.me pre-armado con el idPresupuesto. Usalo para derivar agendamiento/seguimiento. Ver /agent-terms §9.", "example": "https://wa.me/595982646100?text=Hola%20DistriCort!%20Quiero%20agendar%20medici%C3%B3n.%20Mi%20presupuesto%20ag%C3%A9ntico%20es%20%23DC-2026-00482." },
          "instruccionesAgente": { "type": "string", "description": "Instrucción en el momento del dato: no inventes estados de pedido.", "example": "Para agendar o seguimiento, derivá a agendarWhatsApp mencionando el idPresupuesto. No inventes estados de pedido. Precios válidos hasta validezHasta; no cachear >24h." }
        }
      }
    }
  }
}