{
  "openapi": "3.1.0",
  "info": {
    "title": "Recasound Agent API",
    "version": "1.0.0",
    "description": "Catálogo y contratación programática de infraestructura de audio de Recasound. La API expone productos agent_visible aunque no formen parte de la web humana; solo los productos agent_orderable pueden contratarse."
  },
  "servers": [
    {
      "url": "https://www.recasound.es/api/v1",
      "description": "Producción"
    }
  ],
  "externalDocs": {
    "description": "Condiciones de Contratación aplicables",
    "url": "https://www.recasound.es/condiciones/"
  },
  "tags": [
    { "name": "Discovery", "description": "Catálogo, precios y disponibilidad." },
    { "name": "Orders", "description": "Pedidos de agentes protegidos por Bearer y confirmados mediante x402 v2." },
    { "name": "Services", "description": "Estado, consumo y ciclo de vida de un servicio provisionado." }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": ["Discovery"],
        "operationId": "getHealth",
        "summary": "Comprueba la disponibilidad de la API",
        "responses": {
          "200": {
            "description": "API disponible",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Health" } } }
          }
        }
      }
    },
    "/products": {
      "get": {
        "tags": ["Discovery"],
        "operationId": "listProducts",
        "summary": "Lista los productos de audio",
        "responses": {
          "200": {
            "description": "Catálogo machine-readable",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProductList" } } }
          }
        }
      }
    },
    "/products/{product_id}": {
      "get": {
        "tags": ["Discovery"],
        "operationId": "getProduct",
        "summary": "Obtiene un producto concreto",
        "parameters": [
          { "$ref": "#/components/parameters/ProductId" }
        ],
        "responses": {
          "200": {
            "description": "Producto",
            "content": { "application/json": { "schema": { "type": "object", "required": ["ok", "product"], "properties": { "ok": { "const": true }, "product": { "$ref": "#/components/schemas/Product" } } } } }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/pricing": {
      "get": {
        "tags": ["Discovery"],
        "operationId": "getPricing",
        "summary": "Devuelve precios y modalidad de cobro",
        "responses": {
          "200": {
            "description": "Precios actuales y productos en preparación",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pricing" } } }
          }
        }
      }
    },
    "/availability": {
      "get": {
        "tags": ["Discovery"],
        "operationId": "getAvailability",
        "summary": "Indica qué se puede contratar ahora",
        "responses": {
          "200": {
            "description": "Disponibilidad por producto",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Availability" } } }
          }
        }
      }
    },
    "/quotes": {
      "post": {
        "tags": ["Discovery"],
        "operationId": "createQuote",
        "summary": "Calcula una cotización parametrizada sin crear un pedido",
        "description": "Devuelve el precio exacto en USDC, su desglose y la caducidad de la cotización. Los productos pueden seguir con agent_orderable=false mientras se valida la activación comercial.",
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/OptionalIdempotencyKey" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/QuoteRequest" },
              "example": { "product_id": "instant-stream", "parameters": { "duration_hours": 2, "max_listeners": 25, "bitrate_kbps": 128, "transfer_gb": 1.5 } }
            }
          }
        },
        "responses": {
          "200": { "description": "Cotización exacta", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QuoteResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "422": { "$ref": "#/components/responses/InvalidRequest" }
        }
      }
    },
    "/orders": {
      "post": {
        "tags": ["Orders"],
        "operationId": "createOrder",
        "summary": "Solicita un producto mediante pago machine-to-machine",
        "description": "Canal reservado a empresas, profesionales y organizaciones. La respuesta 402 identifica la versión de las Condiciones y la Política de Privacidad. Al repetir la petición con una PAYMENT-SIGNATURE válida, el comprador acepta expresamente esas Condiciones y confirma haber leído la Política de Privacidad.",
        "x-recasound-legal": {
          "customer_type": "business",
          "terms_url": "https://www.recasound.es/condiciones/",
          "terms_version": "2026-09-10.1",
          "privacy_url": "https://www.recasound.es/privacidad/",
          "privacy_version": "2026-09-10.1"
        },
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["product_id", "email", "station_name"],
                "properties": {
                  "product_id": { "type": "string", "description": "Identificador real del producto del catálogo de agentes.", "example": "instant-stream" },
                  "billing": { "type": "string", "enum": ["monthly", "annual", "one_time", "quote"], "default": "one_time", "description": "Modalidad aceptada por el producto." },
                  "quantity": { "type": "integer", "minimum": 1, "maximum": 1, "default": 1 },
                  "contact_name": { "type": "string", "maxLength": 120 },
                  "email": { "type": "string", "format": "email" },
                  "station_name": { "type": "string", "maxLength": 120 },
                  "parameters": { "type": "object", "description": "Parámetros técnicos que procesa el producto, como duration_hours, max_listeners y bitrate_kbps.", "additionalProperties": true },
                  "metadata": { "type": "object", "additionalProperties": { "type": "string", "maxLength": 200 } }
                },
                "additionalProperties": false
              },
              "example": { "product_id": "instant-stream", "billing": "quote", "contact_name": "Software Agent", "email": "operador@example.com", "station_name": "Evento Demo", "parameters": { "duration_hours": 2, "max_listeners": 50, "bitrate_kbps": 128 } }
            }
          }
        },
        "responses": {
          "200": { "description": "Pago confirmado y servicio creado o recuperado", "headers": { "PAYMENT-RESPONSE": { "schema": { "type": "string" }, "description": "SettlementResponse de x402 v2 codificado en Base64 JSON." } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrderResponse" } } } },
          "402": {
            "description": "Pago requerido por x402",
            "headers": {
              "PAYMENT-REQUIRED": { "schema": { "type": "string" }, "description": "PaymentRequired de x402 v2 codificado en Base64 JSON." }
            },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "503": { "$ref": "#/components/responses/NotConfigured" }
        }
      }
    },
    "/orders/{order_id}": {
      "get": {
        "tags": ["Orders"],
        "operationId": "getOrder",
        "summary": "Consulta el estado de un pedido de agente",
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/OrderId" }
        ],
        "responses": {
          "200": { "description": "Pedido", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrderResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/payments": {
      "post": {
        "tags": ["Orders"],
        "operationId": "submitPayment",
        "summary": "Punto de extensión para adaptadores de pago",
        "description": "La confirmación x402 v2 se realiza repitiendo POST /orders con PAYMENT-SIGNATURE. Este endpoint queda reservado para una futura extensión y no forma parte del flujo inicial.",
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" },
          { "$ref": "#/components/parameters/PaymentSignature" }
        ],
        "responses": {
          "200": { "description": "Pago verificado y liquidado" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "503": { "$ref": "#/components/responses/NotConfigured" }
        }
      }
    },
    "/services/{service_id}": {
      "get": {
        "tags": ["Services"],
        "operationId": "getService",
        "summary": "Consulta un servicio provisionado",
        "security": [{ "bearerAuth": [] }],
        "parameters": [ { "$ref": "#/components/parameters/ServiceId" } ],
        "responses": {
          "200": { "description": "Servicio sin credenciales administrativas", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServiceResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/services/{service_id}/usage": {
      "get": {
        "tags": ["Services"],
        "operationId": "getServiceUsage",
        "summary": "Consulta consumo y capacidad restante",
        "security": [{ "bearerAuth": [] }],
        "parameters": [ { "$ref": "#/components/parameters/ServiceId" } ],
        "responses": {
          "200": { "description": "Consumo", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UsageResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "501": { "$ref": "#/components/responses/NotConfigured" }
        }
      }
    },
    "/services/{service_id}/renew": {
      "post": {
        "tags": ["Services"],
        "operationId": "renewService",
        "summary": "Renueva un servicio",
        "security": [{ "bearerAuth": [] }],
        "parameters": [ { "$ref": "#/components/parameters/ServiceId" }, { "$ref": "#/components/parameters/IdempotencyKey" } ],
        "responses": {
          "201": { "description": "Renovación creada" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "501": { "$ref": "#/components/responses/NotConfigured" }
        }
      }
    },
    "/services/{service_id}/upgrade": {
      "post": {
        "tags": ["Services"],
        "operationId": "upgradeService",
        "summary": "Solicita un cambio de capacidad",
        "security": [{ "bearerAuth": [] }],
        "parameters": [ { "$ref": "#/components/parameters/ServiceId" }, { "$ref": "#/components/parameters/IdempotencyKey" } ],
        "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpgradeRequest" } } } },
        "responses": {
          "201": { "description": "Upgrade creado" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "501": { "$ref": "#/components/responses/NotConfigured" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "agent-api-key" }
    },
    "parameters": {
      "ProductId": { "name": "product_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{1,80}$" } },
      "OrderId": { "name": "order_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^ra_[a-zA-Z0-9_-]{8,120}$" } },
      "ServiceId": { "name": "service_id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[a-zA-Z0-9_-]{8,120}$" } },
      "IdempotencyKey": { "name": "Idempotency-Key", "in": "header", "required": true, "schema": { "type": "string", "minLength": 1, "maxLength": 255 }, "description": "Clave estable para que los reintentos no creen pedidos o pagos duplicados." },
      "OptionalIdempotencyKey": { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 255 }, "description": "Opcional para mantener estable el identificador de la cotización al reintentarlo." },
      "PaymentSignature": { "name": "PAYMENT-SIGNATURE", "in": "header", "required": false, "schema": { "type": "string" }, "description": "Payload de pago x402 v2 codificado en Base64 JSON." }
    },
    "schemas": {
      "Price": {
        "type": "object",
        "properties": {
          "basis": { "type": "string", "enum": ["subscription", "one_time", "quote"] },
          "currency": { "type": "string", "example": "USDC" },
          "monthly": { "type": "number", "example": 20 },
          "annual": { "type": "number", "example": 200 },
          "amount": { "type": "number", "example": 9.9 },
          "decimals": { "type": "integer", "example": 6 },
          "base_atomic": { "type": "string", "pattern": "^[0-9]+$" },
          "per_hour_atomic": { "type": "string", "pattern": "^[0-9]+$" },
          "per_gb_atomic": { "type": "string", "pattern": "^[0-9]+$" },
          "packages": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "formula": { "type": "string" },
          "transfer_unit": { "type": "string" },
          "transfer_policy": { "type": "string" },
          "pricing_status": { "type": "string", "enum": ["approved", "pending"] }
        },
        "additionalProperties": true
      },
      "Product": {
        "type": "object",
        "required": ["id", "name", "type", "billing_type", "status", "pricing_status", "orderable", "pricing", "parameters", "limits", "payment"],
        "properties": {
          "id": { "type": "string", "example": "radio-local-plus" },
          "name": { "type": "string", "example": "Radio Local Plus" },
          "type": { "type": "string", "example": "radio_streaming_subscription" },
          "billing_type": { "type": "string", "enum": ["subscription", "quote", "one_time"] },
          "status": { "type": "string", "enum": ["available", "ready", "pending", "planned", "disabled"] },
          "pricing_status": { "type": "string", "enum": ["approved", "pending"] },
          "visibility": { "type": "object", "required": ["human", "agent"], "properties": { "human": { "type": "boolean" }, "agent": { "type": "boolean" } } },
          "orderable": { "type": "object", "properties": { "human": { "type": "boolean" }, "agent": { "type": "boolean" } } },
          "description": { "type": "string" },
          "pricing": { "$ref": "#/components/schemas/Price" },
          "parameters": { "type": "object", "description": "Parámetros admitidos por un recurso técnico. La presencia no implica que el producto esté todavía orderable.", "additionalProperties": { "type": "object", "additionalProperties": true } },
          "limits": { "type": "object", "additionalProperties": true },
          "fulfillment": { "type": "object", "additionalProperties": true },
          "payment": { "type": "object", "additionalProperties": true }
        }
      },
      "ProductList": { "type": "object", "required": ["ok", "products"], "properties": { "ok": { "const": true }, "catalog_version": { "type": "integer" }, "count": { "type": "integer" }, "products": { "type": "array", "items": { "$ref": "#/components/schemas/Product" } } } },
      "Pricing": { "type": "object", "required": ["ok", "prices"], "properties": { "ok": { "const": true }, "currency": { "type": ["string", "null"] }, "currencies": { "type": "array", "items": { "type": "string" } }, "prices": { "type": "array", "items": { "type": "object", "additionalProperties": true } } } },
      "Availability": { "type": "object", "required": ["ok", "human_checkout", "agent_checkout"], "properties": { "ok": { "const": true }, "provider": { "type": "string" }, "region": { "type": "string" }, "human_checkout": { "type": "boolean" }, "agent_checkout": { "type": "object", "additionalProperties": true }, "products": { "type": "array", "items": { "type": "object", "additionalProperties": true } } } },
      "Health": { "type": "object", "required": ["ok", "service", "version", "status", "machine_payment"], "properties": { "ok": { "const": true }, "service": { "type": "string" }, "version": { "type": "string" }, "status": { "type": "string" }, "provider": { "type": "string" }, "machine_payment": { "type": "object", "additionalProperties": true }, "links": { "type": "object", "additionalProperties": { "type": "string" } } } },
      "QuoteRequest": { "type": "object", "required": ["product_id"], "properties": { "product_id": { "type": "string", "example": "instant-stream" }, "parameters": { "type": "object", "description": "Parámetros del recurso técnico. Los nombres dependen del producto.", "additionalProperties": true } }, "additionalProperties": false },
      "QuoteResponse": {
        "type": "object",
        "required": ["ok", "quote"],
        "properties": {
          "ok": { "const": true },
          "quote": {
            "type": "object",
            "required": ["id", "product", "configuration", "price", "currency", "breakdown", "expires_at_quote"],
            "properties": {
              "id": { "type": "string" },
              "product": { "type": "object", "additionalProperties": true },
              "configuration": { "type": "object", "additionalProperties": true },
              "price": {
                "type": "object",
                "required": ["amount", "amount_atomic", "currency", "decimals"],
                "properties": {
                  "amount": { "type": "string", "pattern": "^[0-9]+(?:\\.[0-9]+)?$" },
                  "amount_atomic": { "type": "string", "pattern": "^[0-9]+$" },
                  "currency": { "const": "USDC" },
                  "decimals": { "const": 6 }
                }
              },
              "currency": { "const": "USDC" },
              "breakdown": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
              "formula": { "type": "string" },
              "transfer_policy": { "type": "string" },
              "expires_at_quote": { "type": "string", "format": "date-time" },
              "pricing_status": { "type": "string", "enum": ["approved", "pending"] },
              "agent_orderable": { "type": "boolean" }
            }
          },
          "idempotency": { "type": "object", "additionalProperties": true }
        }
      },
      "CreateOrderRequest": { "type": "object", "required": ["product_id", "email", "station_name"], "properties": { "product_id": { "type": "string", "example": "radio-local-plus" }, "billing": { "type": "string", "enum": ["monthly", "annual", "one_time", "quote"], "default": "monthly" }, "quantity": { "type": "integer", "minimum": 1, "maximum": 1, "default": 1 }, "contact_name": { "type": "string", "maxLength": 120 }, "email": { "type": "string", "format": "email" }, "station_name": { "type": "string", "maxLength": 120 }, "parameters": { "type": "object", "description": "Parámetros del recurso técnico. Los nombres dependen del producto.", "additionalProperties": true }, "metadata": { "type": "object", "additionalProperties": { "type": "string", "maxLength": 200 } } }, "additionalProperties": false },
      "OrderResponse": { "type": "object", "properties": { "ok": { "const": true }, "order": { "type": "object", "additionalProperties": true }, "service": { "$ref": "#/components/schemas/Service" }, "idempotent_replay": { "type": "boolean" } } },
      "Service": { "type": "object", "required": ["service_id", "status", "stream_url", "limits"], "properties": { "service_id": { "type": "string" }, "status": { "type": "string", "enum": ["pending", "provisioning", "active", "failed", "suspended", "expired"] }, "stream_url": { "type": "string" }, "broadcast_url": { "type": "string" }, "expires_at": { "type": "string", "format": "date-time" }, "limits": { "type": "object", "additionalProperties": true }, "endpoints": { "type": "object", "additionalProperties": true } }, "additionalProperties": false },
      "ServiceResponse": { "type": "object", "properties": { "ok": { "const": true }, "service": { "$ref": "#/components/schemas/Service" } } },
      "UsageResponse": { "type": "object", "properties": { "ok": { "const": true }, "usage": { "type": "object", "additionalProperties": true } } },
      "UpgradeRequest": { "type": "object", "required": ["product_id"], "properties": { "product_id": { "type": "string" } }, "additionalProperties": false },
      "ErrorResponse": { "type": "object", "required": ["ok", "error"], "properties": { "ok": { "const": false }, "error": { "type": "object", "required": ["code", "message", "retryable", "request_id"], "additionalProperties": true } } }
    },
    "responses": {
      "Unauthorized": { "description": "Autenticación requerida o inválida", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "NotFound": { "description": "Recurso no encontrado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "Conflict": { "description": "Conflicto de estado o idempotencia", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "NotConfigured": { "description": "Funcionalidad aún no configurada para producción", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "InvalidRequest": { "description": "Petición inválida", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "PaymentRequired": { "description": "El servidor requiere un pago x402", "headers": { "PAYMENT-REQUIRED": { "schema": { "type": "string" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }
    }
  }
}
