{
  "openapi": "3.1.0",
  "info": {
    "title": "Redfloral Partner API",
    "license": {
      "name": "Uso restringido a socios de Redfloral",
      "identifier": "LicenseRef-Redfloral-Partner"
    },
    "version": "1.0.0-beta",
    "summary": "API para florerías socias de Redfloral.",
    "description": "Catálogo, disponibilidad de despacho, pedidos y webhooks de una florería socia.\nEl socio manda sus pedidos a Redfloral como **venta local** (cobrados en su propio\nsistema), Redfloral los despacha y le avisa de los cambios por webhook.\n\n**Beta durante el piloto**: el contrato puede cambiar con aviso directo a los socios;\ndespués se congela y solo se agregan campos opcionales. Los campos desconocidos del\nbody se ignoran.\n\nGuías (flujo, casos que sorprenden, webhooks y verificación de firma):\nhttps://docs.redfloral.cl\n\n**Autenticación**: `Authorization: Bearer rf_live_<prefix>_<secreto>`. Una key pertenece\na una florería; nunca se manda un `company_id`. **Límites**: 120 solicitudes/minuto de\nlectura y 120/hora de escritura por key (429 con `Retry-After`). Las respuestas que\npasan por el límite traen `X-RateLimit-Limit` y `X-RateLimit-Remaining`; los 401 y 403\nde autenticación o scope no los traen.\n\n**Scopes**: `catalog:read`, `orders:read`, `orders:write` (los tres por defecto). Cada\noperación declara los suyos en `x-required-scopes`.\n\n**Listas paginadas**: `?page=` y `?page_size=` (máx. 100, por defecto 50).\n`zones`, `schedules` y `webhooks` devuelven todo en una sola página.\n"
  },
  "servers": [
    {
      "url": "https://api.redfloral.cl/api/partner/v1",
      "description": "Producción"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "x-example-values": {
    "order_item": {
      "id": 70123,
      "external_id": "WC-10452",
      "external_url": "https://mitienda.cl/wp-admin/post.php?post=10452",
      "status": {
        "slug": "processing",
        "name": "Aceptado"
      },
      "is_internal": true,
      "created_by_api": true,
      "secure_key": "6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
      "status_url": "https://redfloral.cl/estado-del-pedido/6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
      "shipping_date": "2026-11-02",
      "schedule_id": 31,
      "schedule_text": "Entre 10:00 y 14:00 hrs",
      "zone_id": 12,
      "zone_text": "Ñuñoa",
      "customer": {
        "name": "Ana Pérez",
        "email": "ana@correo.cl",
        "phone_code": "56",
        "phone": "912345678"
      },
      "address": {
        "type": "DrParticular",
        "name": "María González",
        "street": "Los Aromos",
        "street_number": "1234",
        "more_info": "Depto 501",
        "phone": "987654321",
        "latitude": -33.41,
        "longitude": -70.6
      },
      "card_message": "Feliz cumpleaños",
      "anonymous": false,
      "total_products": 35980,
      "total_shipping": 3990,
      "total_discount": 0,
      "total": 39970,
      "delivered_at": null,
      "created_at": "2026-10-23T15:04:05Z",
      "updated_at": "2026-10-23T15:04:05Z",
      "warnings": []
    },
    "order_detail": {
      "id": 70123,
      "external_id": "WC-10452",
      "external_url": "https://mitienda.cl/wp-admin/post.php?post=10452",
      "status": {
        "slug": "processing",
        "name": "Aceptado"
      },
      "is_internal": true,
      "created_by_api": true,
      "secure_key": "6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
      "status_url": "https://redfloral.cl/estado-del-pedido/6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
      "shipping_date": "2026-11-02",
      "schedule_id": 31,
      "schedule_text": "Entre 10:00 y 14:00 hrs",
      "zone_id": 12,
      "zone_text": "Ñuñoa",
      "customer": {
        "name": "Ana Pérez",
        "email": "ana@correo.cl",
        "phone_code": "56",
        "phone": "912345678"
      },
      "address": {
        "type": "DrParticular",
        "name": "María González",
        "street": "Los Aromos",
        "street_number": "1234",
        "more_info": "Depto 501",
        "phone": "987654321",
        "latitude": -33.41,
        "longitude": -70.6
      },
      "card_message": "Feliz cumpleaños",
      "anonymous": false,
      "total_products": 35980,
      "total_shipping": 3990,
      "total_discount": 0,
      "total": 39970,
      "delivered_at": null,
      "created_at": "2026-10-23T15:04:05Z",
      "updated_at": "2026-10-23T15:04:05Z",
      "warnings": [],
      "url_photo": "https://redfloral.cl/foto-de-entrega/6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
      "photos": null,
      "products": [
        {
          "id": 1,
          "product_id": 8891,
          "variant_id": null,
          "shared": false,
          "name": "Ramo Primavera",
          "quantity": 1,
          "price": 29990,
          "note": "",
          "options": []
        },
        {
          "id": 2,
          "product_id": null,
          "variant_id": null,
          "shared": false,
          "name": "Globo personalizado",
          "quantity": 1,
          "price": 5990,
          "note": "",
          "options": []
        }
      ],
      "history": [
        {
          "status": "processing",
          "status_name": "Aceptado",
          "message": "Pedido creado por API",
          "created_at": "2026-10-23T15:04:05Z"
        }
      ],
      "document_type": "boleta",
      "document": null
    },
    "webhook_item": {
      "id": 3,
      "name": "ERP",
      "url": "https://mitienda.cl/redfloral/webhook",
      "events": [
        "order.status_changed",
        "order.photo_uploaded",
        "product.updated",
        "product.price_changed"
      ],
      "is_active": true,
      "consecutive_failures": 0,
      "disabled_by_failures": false,
      "disabled_reason": null,
      "disabled_at": null,
      "last_delivery_at": null,
      "created_at": "2026-10-23T15:00:00Z",
      "updated_at": "2026-10-23T15:00:00Z",
      "secret": "whsec_Zm9vYmFyYmF6cXV4MTIzNDU2Nzg5MGFiY2RlZg"
    }
  },
  "tags": [
    {
      "name": "Identidad",
      "description": "Validar la key y consultar el plan."
    },
    {
      "name": "Catálogo y despacho",
      "description": "Productos, precios por fecha, comunas, horarios y disponibilidad."
    },
    {
      "name": "Pedidos",
      "description": "Crear, consultar y cancelar pedidos (ventas locales) y subirles la boleta o factura."
    },
    {
      "name": "Webhooks",
      "description": "Gestión de webhooks y consulta de entregas. Los eventos están en la sección `webhooks`."
    }
  ],
  "paths": {
    "/ping": {
      "get": {
        "tags": [
          "Identidad"
        ],
        "operationId": "ping",
        "summary": "Valida la key",
        "description": "Devuelve la florería y los scopes de la key. No exige ningún scope.",
        "x-required-scopes": [],
        "responses": {
          "200": {
            "description": "Key válida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ping"
                },
                "example": {
                  "error": false,
                  "company_id": 15,
                  "company_name": "Mi Florería",
                  "key": {
                    "name": "Tienda WooCommerce",
                    "prefix": "a1b2c3d4"
                  },
                  "scopes": [
                    "orders:read",
                    "orders:write",
                    "catalog:read"
                  ],
                  "server_time": "2026-10-23T15:04:05.123456Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/me": {
      "get": {
        "tags": [
          "Identidad"
        ],
        "operationId": "getMe",
        "summary": "Plan y cupo de ventas locales",
        "description": "Permite avisar **antes** de que la florería quede bloqueada por excedente sin\nliquidar. Cada pedido por API consume cupo de venta local.\n",
        "x-required-scopes": [
          "orders:read"
        ],
        "responses": {
          "200": {
            "description": "Estado del plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                },
                "example": {
                  "error": false,
                  "company_id": 15,
                  "plan": "Plan Pro",
                  "api_access": true,
                  "orders_write_enabled": true,
                  "free_orders_remaining": 12,
                  "next_order_cost": 0,
                  "pending_count": 0,
                  "pending_limit": 20,
                  "is_blocked": false
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/zones": {
      "get": {
        "tags": [
          "Catálogo y despacho"
        ],
        "operationId": "listZones",
        "summary": "Comunas activas de la florería",
        "description": "`id` es el `zone_id` del `POST /orders`. `name_cleaned` (minúsculas, sin acentos) sirve\npara mapear la comuna del cliente del socio.\n",
        "x-required-scopes": [
          "catalog:read"
        ],
        "responses": {
          "200": {
            "description": "Todas las comunas, en una sola página.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Zone"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": false,
                  "data": [
                    {
                      "id": 12,
                      "name": "Ñuñoa",
                      "name_cleaned": "nunoa",
                      "price": 2500,
                      "time_end": "15:00",
                      "is_retirement": false
                    }
                  ],
                  "count": 1,
                  "has_more": false,
                  "page": 1,
                  "page_size": 1
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/schedules": {
      "get": {
        "tags": [
          "Catálogo y despacho"
        ],
        "operationId": "listSchedules",
        "summary": "Plantilla semanal de horarios",
        "description": "`id` es el `shipping_schedule_id` del `POST /orders`. Para los horarios de un día\nconcreto usa `/delivery-availability`.\n",
        "x-required-scopes": [
          "catalog:read"
        ],
        "responses": {
          "200": {
            "description": "Todos los horarios activos, en una sola página.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Schedule"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": false,
                  "data": [
                    {
                      "id": 7,
                      "day": 0,
                      "type_day": "AN",
                      "time_start": "10:00",
                      "time_end": "14:00",
                      "time_close": "09:00",
                      "price": 1000
                    }
                  ],
                  "count": 1,
                  "has_more": false,
                  "page": 1,
                  "page_size": 1
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/delivery-availability": {
      "get": {
        "tags": [
          "Catálogo y despacho"
        ],
        "operationId": "getDeliveryAvailability",
        "summary": "Disponibilidad de despacho de una fecha y comuna",
        "description": "Mismas reglas que la web (día hábil, bloqueo de día, hora de corte, horarios abiertos,\nproducto inactivo o excluido de alta demanda). Indica `zone_id` o `zone_text`; si el\ntexto no coincide exactamente (sin importar mayúsculas ni acentos) responde 200 con\n`zone_id: null`, `available: false` y `reason: zone_not_matched`. No se cachea.\n",
        "x-required-scopes": [
          "catalog:read"
        ],
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-11-02"
          },
          {
            "name": "zone_id",
            "in": "query",
            "description": "Id de `GET /zones`. Manda sobre `zone_text`.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "zone_text",
            "in": "query",
            "description": "Comuna en texto.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "product_id",
            "in": "query",
            "description": "Considera producto inactivo / excluido de alta demanda.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Disponibilidad.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryAvailability"
                },
                "example": {
                  "error": false,
                  "date": "2026-11-02",
                  "zone_id": 12,
                  "zone_name": "Ñuñoa",
                  "available": true,
                  "reason": null,
                  "schedules": [
                    {
                      "id": 7,
                      "time_start": "10:00",
                      "time_end": "14:00",
                      "price": 3500
                    }
                  ],
                  "high_demand": false,
                  "shipping_cost_base": 2500
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/products": {
      "get": {
        "tags": [
          "Catálogo y despacho"
        ],
        "operationId": "listProducts",
        "summary": "Productos de la florería, incluidos los que revende",
        "description": "Los revendidos llevan `shared: true` (precio, oferta, stock y estado del compartido;\nnombre, descripción y foto del original). El `id` es siempre el del producto original\ny es el `product_id` del `POST /orders`. Sin `is_active` lista activos e inactivos.\nSi Redfloral configuró la reventa para usar el precio y el stock del original, `price`,\n`price_sale`, `sale_date_end`, `is_stock` y `quantity` son los del original y `is_active`\nes `false` mientras el original esté inactivo o sin stock (sin cambio de forma).\n",
        "x-required-scopes": [
          "catalog:read"
        ],
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "is_active",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          }
        ],
        "responses": {
          "200": {
            "description": "Página de productos.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Product"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": false,
                  "data": [
                    {
                      "id": 8891,
                      "shared": false,
                      "name": "Ramo Primavera",
                      "description": "Rosas y lirios.",
                      "type": 0,
                      "price": 25000,
                      "price_sale": null,
                      "sale_date_end": null,
                      "is_active": true,
                      "is_stock": true,
                      "quantity": 7,
                      "is_exclude_high_demand": false,
                      "photo": "https://storage.googleapis.com/redfloral-bucket/p/8891.jpg",
                      "updated_at": "2026-10-23T12:00:00Z"
                    }
                  ],
                  "count": 1,
                  "has_more": false,
                  "page": 1,
                  "page_size": 50
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/products/prices": {
      "get": {
        "tags": [
          "Catálogo y despacho"
        ],
        "operationId": "getProductPrices",
        "summary": "Precio final por fecha de despacho",
        "description": "Misma fórmula del carrito: oferta vigente ese día y alta demanda de la florería.\nSin `ids` pagina todo el catálogo. En productos tipo 3 los precios van en `variants` y\nen tipo 2 en `options` (`price*` es `null` en esos tipos).\n",
        "x-required-scopes": [
          "catalog:read"
        ],
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "ids",
            "in": "query",
            "description": "Ids separados por coma (máx. 100).",
            "schema": {
              "type": "string"
            },
            "example": "8891,8892"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          }
        ],
        "responses": {
          "200": {
            "description": "Precios por producto.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "required": [
                        "date",
                        "high_demand_percentage"
                      ],
                      "properties": {
                        "date": {
                          "type": "string",
                          "format": "date"
                        },
                        "high_demand_percentage": {
                          "type": "integer"
                        },
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ProductPrice"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": false,
                  "date": "2026-11-02",
                  "high_demand_percentage": 10,
                  "data": [
                    {
                      "id": 8891,
                      "shared": false,
                      "type": 0,
                      "price": 27500,
                      "price_sale": 24750,
                      "final_price": 24750,
                      "high_demand_percentage": 10
                    }
                  ],
                  "count": 1,
                  "has_more": false,
                  "page": 1,
                  "page_size": 100
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/products/{id}": {
      "get": {
        "tags": [
          "Catálogo y despacho"
        ],
        "operationId": "getProduct",
        "summary": "Detalle de un producto",
        "description": "Agrega imágenes, variantes y opciones al ítem del listado.",
        "x-required-scopes": [
          "catalog:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Producto.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "product"
                  ],
                  "properties": {
                    "error": {
                      "type": "boolean",
                      "const": false
                    },
                    "product": {
                      "$ref": "#/components/schemas/ProductDetail"
                    }
                  }
                },
                "example": {
                  "error": false,
                  "product": {
                    "id": 8891,
                    "shared": false,
                    "name": "Ramo Primavera",
                    "description": "Rosas y lirios.",
                    "type": 0,
                    "price": 25000,
                    "price_sale": null,
                    "sale_date_end": null,
                    "is_active": true,
                    "is_stock": true,
                    "quantity": 7,
                    "is_exclude_high_demand": false,
                    "photo": "https://storage.googleapis.com/redfloral-bucket/p/8891.jpg",
                    "updated_at": "2026-10-23T12:00:00Z",
                    "is_message": true,
                    "message_max": 200,
                    "message_label": "Mensaje de la tarjeta",
                    "personalized_min": null,
                    "personalized_max": null,
                    "personalized_max_combinations": 2,
                    "images": [
                      {
                        "id": 1,
                        "url": "https://storage.googleapis.com/x/1.jpg",
                        "small": null,
                        "medium": "https://storage.googleapis.com/x/1m.jpg",
                        "large": null
                      }
                    ],
                    "variants": [],
                    "options": []
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/orders": {
      "get": {
        "tags": [
          "Pedidos"
        ],
        "operationId": "listOrders",
        "summary": "Pedidos de la florería",
        "description": "Incluye también los del marketplace y de la tienda de la florería. Se ven los mismos\nestados que en el panel. Para sincronizar usa `updated_since` (estricto `>`).\n",
        "x-required-scopes": [
          "orders:read"
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Uno o varios estados separados por coma.",
            "schema": {
              "type": "string"
            },
            "example": "processing,in_transit,delivered"
          },
          {
            "name": "shipping_date_from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "shipping_date_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "external_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "is_internal",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "created_by_api",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          }
        ],
        "responses": {
          "200": {
            "description": "Página de pedidos, más recientes primero.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Order"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": false,
                  "data": [
                    {
                      "id": 70123,
                      "external_id": "WC-10452",
                      "external_url": "https://mitienda.cl/wp-admin/post.php?post=10452",
                      "status": {
                        "slug": "processing",
                        "name": "Aceptado"
                      },
                      "is_internal": true,
                      "created_by_api": true,
                      "secure_key": "6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
                      "status_url": "https://redfloral.cl/estado-del-pedido/6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
                      "shipping_date": "2026-11-02",
                      "schedule_id": 31,
                      "schedule_text": "Entre 10:00 y 14:00 hrs",
                      "zone_id": 12,
                      "zone_text": "Ñuñoa",
                      "customer": {
                        "name": "Ana Pérez",
                        "email": "ana@correo.cl",
                        "phone_code": "56",
                        "phone": "912345678"
                      },
                      "address": {
                        "type": "DrParticular",
                        "name": "María González",
                        "street": "Los Aromos",
                        "street_number": "1234",
                        "more_info": "Depto 501",
                        "phone": "987654321",
                        "latitude": -33.41,
                        "longitude": -70.6
                      },
                      "card_message": "Feliz cumpleaños",
                      "anonymous": false,
                      "total_products": 35980,
                      "total_shipping": 3990,
                      "total_discount": 0,
                      "total": 39970,
                      "delivered_at": null,
                      "created_at": "2026-10-23T15:04:05Z",
                      "updated_at": "2026-10-23T15:04:05Z",
                      "warnings": []
                    }
                  ],
                  "count": 1,
                  "has_more": false,
                  "page": 1,
                  "page_size": 50
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Pedidos"
        ],
        "operationId": "createOrder",
        "summary": "Crea un pedido (venta local)",
        "description": "Consume cupo de venta local y cobra excedente igual que el panel. Los totales se\nrecalculan en el servidor (`total = Σ price × quantity - discount + shipping_cost`); el\nprecio lo manda el socio. `discount` (opcional) es el descuento total del pedido en monto\ny no puede superar `total_products`. Lo que no se puede resolver (comuna, horario, fecha) **no falla**: el\npedido se crea y la respuesta lleva `warnings`. Reintento con el mismo `external_id`\n→ 200 con el pedido existente. Requiere además que la florería tenga habilitado el\ningreso de ventas locales.\n\n`customer.email`, `customer.phone` y `shipping_cost` son obligatorios (`shipping_cost`\nmínimo 0; `0` es válido). Un producto que la florería revende (`shared: true`) queda\nvinculado al compartido y su stock se mueve igual que en el checkout web.\n\nOrden de validación: scope → body (400) → idempotencia (200) → zona/horario/productos\n(400) → bloqueo por excedente (422, no se crea nada).\n",
        "x-required-scopes": [
          "orders:write"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderCreate"
              },
              "example": {
                "external_id": "WC-10452",
                "external_url": "https://mitienda.cl/wp-admin/post.php?post=10452",
                "customer": {
                  "name": "Ana Pérez",
                  "email": "ana@correo.cl",
                  "phone": "912345678",
                  "phone_code": "56"
                },
                "address": {
                  "zone_text": "Ñuñoa",
                  "zone_id": 12,
                  "type": "DrParticular",
                  "name": "María González",
                  "street": "Los Aromos",
                  "street_number": "1234",
                  "more_info": "Depto 501",
                  "phone": "987654321",
                  "latitude": -33.41,
                  "longitude": -70.6
                },
                "shipping_date": "2026-11-02",
                "shipping_schedule_id": 7,
                "shipping_cost": 3990,
                "discount": 0,
                "card_message": "Feliz cumpleaños",
                "anonymous": false,
                "document_type": "boleta",
                "products": [
                  {
                    "product_id": 8891,
                    "variant_id": null,
                    "quantity": 1,
                    "price": 29990,
                    "note": ""
                  },
                  {
                    "product_id": 8892,
                    "quantity": 1,
                    "options": [
                      {
                        "id": 501,
                        "quantity": 10
                      }
                    ]
                  },
                  {
                    "name": "Globo personalizado",
                    "quantity": 1,
                    "price": 5990
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reintento: ya existía un pedido con ese `external_id` (`created: false`), o\nrepetición de una `Idempotency-Key` (header `Idempotent-Replayed: true`, misma\nrespuesta que la original).\n",
            "headers": {
              "Idempotent-Replayed": {
                "description": "`true` cuando la respuesta viene de una `Idempotency-Key` repetida.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderResponse"
                }
              }
            }
          },
          "201": {
            "description": "Pedido creado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderResponse"
                },
                "example": {
                  "error": false,
                  "created": true,
                  "order": {
                    "id": 70123,
                    "external_id": "WC-10452",
                    "external_url": "https://mitienda.cl/wp-admin/post.php?post=10452",
                    "status": {
                      "slug": "processing",
                      "name": "Aceptado"
                    },
                    "is_internal": true,
                    "created_by_api": true,
                    "secure_key": "6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
                    "status_url": "https://redfloral.cl/estado-del-pedido/6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
                    "shipping_date": "2026-11-02",
                    "schedule_id": 31,
                    "schedule_text": "Entre 10:00 y 14:00 hrs",
                    "zone_id": 12,
                    "zone_text": "Ñuñoa",
                    "customer": {
                      "name": "Ana Pérez",
                      "email": "ana@correo.cl",
                      "phone_code": "56",
                      "phone": "912345678"
                    },
                    "address": {
                      "type": "DrParticular",
                      "name": "María González",
                      "street": "Los Aromos",
                      "street_number": "1234",
                      "more_info": "Depto 501",
                      "phone": "987654321",
                      "latitude": -33.41,
                      "longitude": -70.6
                    },
                    "card_message": "Feliz cumpleaños",
                    "anonymous": false,
                    "total_products": 35980,
                    "total_shipping": 3990,
                    "total_discount": 0,
                    "total": 39970,
                    "delivered_at": null,
                    "created_at": "2026-10-23T15:04:05Z",
                    "updated_at": "2026-10-23T15:04:05Z",
                    "warnings": [],
                    "url_photo": "https://redfloral.cl/foto-de-entrega/6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
                    "photos": null,
                    "products": [
                      {
                        "id": 1,
                        "product_id": 8891,
                        "variant_id": null,
                        "shared": false,
                        "name": "Ramo Primavera",
                        "quantity": 1,
                        "price": 29990,
                        "note": "",
                        "options": []
                      },
                      {
                        "id": 2,
                        "product_id": null,
                        "variant_id": null,
                        "shared": false,
                        "name": "Globo personalizado",
                        "quantity": 1,
                        "price": 5990,
                        "note": "",
                        "options": []
                      }
                    ],
                    "history": [
                      {
                        "status": "processing",
                        "status_name": "Aceptado",
                        "message": "Pedido creado por API",
                        "created_at": "2026-10-23T15:04:05Z"
                      }
                    ],
                    "document_type": "boleta",
                    "document": null
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "`idempotency_conflict`: la `Idempotency-Key` ya se usó con otro cuerpo, o hay otra\nsolicitud en curso con ella.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": true,
                  "code": "idempotency_conflict",
                  "message": "La Idempotency-Key ya se usó con un cuerpo distinto."
                }
              }
            }
          },
          "422": {
            "description": "`internal_orders_blocked`: la florería alcanzó el límite de excedentes sin liquidar.\nNo se creó nada.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": true,
                  "code": "internal_orders_blocked",
                  "message": "Alcanzaste el límite de pedidos con costo de excedente sin liquidar. Liquida esas ventas para volver a registrar pedidos.",
                  "pending_count": 20,
                  "pending_limit": 20
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/orders/{ref}": {
      "get": {
        "tags": [
          "Pedidos"
        ],
        "operationId": "getOrder",
        "summary": "Detalle de un pedido",
        "x-required-scopes": [
          "orders:read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderRef"
          }
        ],
        "responses": {
          "200": {
            "description": "Pedido con productos, historial y foto de entrega.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "order"
                  ],
                  "properties": {
                    "error": {
                      "type": "boolean",
                      "const": false
                    },
                    "order": {
                      "$ref": "#/components/schemas/OrderDetail"
                    }
                  }
                },
                "example": {
                  "error": false,
                  "order": {
                    "id": 70123,
                    "external_id": "WC-10452",
                    "external_url": "https://mitienda.cl/wp-admin/post.php?post=10452",
                    "status": {
                      "slug": "processing",
                      "name": "Aceptado"
                    },
                    "is_internal": true,
                    "created_by_api": true,
                    "secure_key": "6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
                    "status_url": "https://redfloral.cl/estado-del-pedido/6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
                    "shipping_date": "2026-11-02",
                    "schedule_id": 31,
                    "schedule_text": "Entre 10:00 y 14:00 hrs",
                    "zone_id": 12,
                    "zone_text": "Ñuñoa",
                    "customer": {
                      "name": "Ana Pérez",
                      "email": "ana@correo.cl",
                      "phone_code": "56",
                      "phone": "912345678"
                    },
                    "address": {
                      "type": "DrParticular",
                      "name": "María González",
                      "street": "Los Aromos",
                      "street_number": "1234",
                      "more_info": "Depto 501",
                      "phone": "987654321",
                      "latitude": -33.41,
                      "longitude": -70.6
                    },
                    "card_message": "Feliz cumpleaños",
                    "anonymous": false,
                    "total_products": 35980,
                    "total_shipping": 3990,
                    "total_discount": 0,
                    "total": 39970,
                    "delivered_at": null,
                    "created_at": "2026-10-23T15:04:05Z",
                    "updated_at": "2026-10-23T15:04:05Z",
                    "warnings": [],
                    "url_photo": "https://redfloral.cl/foto-de-entrega/6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
                    "photos": null,
                    "products": [
                      {
                        "id": 1,
                        "product_id": 8891,
                        "variant_id": null,
                        "shared": false,
                        "name": "Ramo Primavera",
                        "quantity": 1,
                        "price": 29990,
                        "note": "",
                        "options": []
                      },
                      {
                        "id": 2,
                        "product_id": null,
                        "variant_id": null,
                        "shared": false,
                        "name": "Globo personalizado",
                        "quantity": 1,
                        "price": 5990,
                        "note": "",
                        "options": []
                      }
                    ],
                    "history": [
                      {
                        "status": "processing",
                        "status_name": "Aceptado",
                        "message": "Pedido creado por API",
                        "created_at": "2026-10-23T15:04:05Z"
                      }
                    ],
                    "document_type": "boleta",
                    "document": null
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/orders/{ref}/cancel": {
      "post": {
        "tags": [
          "Pedidos"
        ],
        "operationId": "cancelOrder",
        "summary": "Cancela un pedido creado por API",
        "description": "Solo pedidos creados por API en `pending` o `processing`. Devuelve el stock de los\nproductos propios con control de stock y el cupo del compartido en los que revende; **no** revierte el\ncosto de excedente. Es idempotente: cancelar un pedido ya cancelado responde 200.\n",
        "x-required-scopes": [
          "orders:write"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderRef"
          }
        ],
        "responses": {
          "200": {
            "description": "Pedido cancelado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "order"
                  ],
                  "properties": {
                    "error": {
                      "type": "boolean",
                      "const": false
                    },
                    "order": {
                      "$ref": "#/components/schemas/OrderDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`order_not_cancellable`: no lo creó la API o ya avanzó de estado (`status` en el cuerpo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": true,
                  "code": "order_not_cancellable",
                  "message": "El pedido ya no se puede cancelar en su estado actual.",
                  "status": "in_transit"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "`service_unavailable`: falta configuración interna en Redfloral.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/orders/{ref}/document": {
      "post": {
        "tags": [
          "Pedidos"
        ],
        "operationId": "uploadOrderDocument",
        "summary": "Sube la boleta o factura del pedido",
        "description": "Sube (o reemplaza) el documento tributario del pedido: archivo, folio y fecha de\nemisión. Vale para **cualquier** pedido de la florería (también los del marketplace,\nque lo necesitan para liquidarse), no solo los creados por API. El tipo de documento\nes el `document_type` del pedido, salvo que mandes `document_type` para corregirlo.\n\nBody `multipart/form-data` (no JSON). El folio no puede repetirse en otro pedido de\nla florería con el mismo tipo de documento (409 `duplicate_document`, con `order_id`\ndel pedido que ya lo tiene). Redfloral revisa el documento con IA en segundo plano:\nla respuesta trae `document.validation = pending` y el resultado aparece después en\n`GET /orders/{ref}`. El cliente recibe el documento por email si mandas\n`notify_customer=true`, o si no lo mandas y la key tiene las notificaciones activadas. No exige la venta local habilitada (`orders_write_disabled`\nno aplica).\n",
        "x-required-scopes": [
          "orders:write"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderRef"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file",
                  "number",
                  "date"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "PDF, JPG o PNG, hasta 10 MB."
                  },
                  "number": {
                    "type": "string",
                    "maxLength": 64,
                    "description": "Folio del documento."
                  },
                  "date": {
                    "type": "string",
                    "format": "date",
                    "description": "Fecha de emisión."
                  },
                  "document_type": {
                    "type": "string",
                    "enum": [
                      "boleta",
                      "factura"
                    ],
                    "description": "Opcional. Cambia el `document_type` del pedido al del documento que subes; sin él, no cambia."
                  },
                  "notify_customer": {
                    "type": "boolean",
                    "description": "Opcional. `true` envía el documento por email al cliente y `false` no, solo en esta subida. Sin él rige la configuración de la key."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Documento guardado; pedido actualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "order"
                  ],
                  "properties": {
                    "error": {
                      "type": "boolean",
                      "const": false
                    },
                    "order": {
                      "$ref": "#/components/schemas/OrderDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`duplicate_document`: el folio ya está en otro pedido de la florería (`order_id` en el cuerpo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": true,
                  "code": "duplicate_document",
                  "message": "El folio 1234 ya está registrado en el pedido #70100.",
                  "order_id": 70100
                }
              }
            }
          },
          "415": {
            "description": "`unsupported_media_type`: el body no es `multipart/form-data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhooks",
        "summary": "Webhooks de la florería",
        "description": "Cualquier key válida gestiona los webhooks de su florería (los scopes solo limitan a\nqué eventos puede suscribirse). Incluye el `secret` para verificar la firma.\n",
        "x-required-scopes": [],
        "responses": {
          "200": {
            "description": "Todos los webhooks, en una sola página.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Webhook"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": false,
                  "data": [
                    {
                      "id": 3,
                      "name": "ERP",
                      "url": "https://mitienda.cl/redfloral/webhook",
                      "events": [
                        "order.status_changed",
                        "order.photo_uploaded",
                        "product.updated",
                        "product.price_changed"
                      ],
                      "is_active": true,
                      "consecutive_failures": 0,
                      "disabled_by_failures": false,
                      "disabled_reason": null,
                      "disabled_at": null,
                      "last_delivery_at": null,
                      "created_at": "2026-10-23T15:00:00Z",
                      "updated_at": "2026-10-23T15:00:00Z",
                      "secret": "whsec_Zm9vYmFyYmF6cXV4MTIzNDU2Nzg5MGFiY2RlZg"
                    }
                  ],
                  "count": 1,
                  "has_more": false,
                  "page": 1,
                  "page_size": 1
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "createWebhook",
        "summary": "Registra un webhook",
        "description": "La URL debe ser https y pública (se rechazan hosts que resuelven a IP privada,\nloopback o link-local, y URLs con credenciales); se vuelve a validar en cada envío.\nCada evento exige su scope (`order.*` → `orders:read`; `product.*` y\n`availability.changed` → `catalog:read`), si no 403 `insufficient_scope`. Los\ncomodines `*`, `order.*`, `product.*` y `availability.*` se expanden al registrar y\n**no incluyen `product.stock_changed`** (opt-in). Máximo 10 por florería.\n",
        "x-required-scopes": [
          "orders:read",
          "catalog:read"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              },
              "example": {
                "url": "https://mitienda.cl/redfloral/webhook",
                "name": "ERP",
                "events": [
                  "order.status_changed",
                  "order.photo_uploaded",
                  "product.updated",
                  "product.price_changed"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook creado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                },
                "example": {
                  "error": false,
                  "webhook": {
                    "id": 3,
                    "name": "ERP",
                    "url": "https://mitienda.cl/redfloral/webhook",
                    "events": [
                      "order.status_changed",
                      "order.photo_uploaded",
                      "product.updated",
                      "product.price_changed"
                    ],
                    "is_active": true,
                    "consecutive_failures": 0,
                    "disabled_by_failures": false,
                    "disabled_reason": null,
                    "disabled_at": null,
                    "last_delivery_at": null,
                    "created_at": "2026-10-23T15:00:00Z",
                    "updated_at": "2026-10-23T15:00:00Z",
                    "secret": "whsec_Zm9vYmFyYmF6cXV4MTIzNDU2Nzg5MGFiY2RlZg"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "`webhook_limit_reached`: máximo 10 webhooks por florería.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/webhooks/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhookDeliveries",
        "summary": "Últimos envíos",
        "description": "Para depurar: resultado, intentos y último error de cada entrega.",
        "x-required-scopes": [],
        "parameters": [
          {
            "name": "webhook_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "event",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "example": "order.created"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/DeliveryStatus"
            }
          },
          {
            "name": "include_payload",
            "in": "query",
            "description": "Agrega el cuerpo enviado.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          }
        ],
        "responses": {
          "200": {
            "description": "Página de entregas, más recientes primero.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookDelivery"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "error": false,
                  "data": [
                    {
                      "id": 9001,
                      "webhook_id": 3,
                      "event": "order.status_changed",
                      "status": "delivered",
                      "attempts": 1,
                      "status_code": 200,
                      "last_error": null,
                      "order_id": 70123,
                      "product_id": null,
                      "next_attempt_at": null,
                      "delivered_at": "2026-11-02T15:04:06Z",
                      "created_at": "2026-11-02T15:04:05Z"
                    }
                  ],
                  "count": 1,
                  "has_more": false,
                  "page": 1,
                  "page_size": 50
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/webhooks/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhook",
        "summary": "Detalle de un webhook",
        "x-required-scopes": [],
        "responses": {
          "200": {
            "description": "Webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "updateWebhook",
        "summary": "Edita un webhook",
        "description": "Todos los campos son opcionales. Reactivar (`is_active: true`) revalida la URL y limpia\n`consecutive_failures` y `disabled_reason`; es la forma de reactivar un webhook\ndesactivado automáticamente.\n",
        "x-required-scopes": [
          "orders:read",
          "catalog:read"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              },
              "example": {
                "is_active": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook actualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhook",
        "summary": "Elimina un webhook",
        "description": "Las entregas pendientes se abandonan.",
        "x-required-scopes": [],
        "responses": {
          "204": {
            "description": "Eliminado."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/webhooks/{id}/rotate-secret": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "rotateWebhookSecret",
        "summary": "Rota el secreto de firma",
        "description": "El secreto anterior deja de valer de inmediato.",
        "x-required-scopes": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook con el `secret` nuevo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "testWebhook",
        "summary": "Envía un evento `ping`",
        "description": "Encola un evento `ping` firmado a la URL del webhook (se entrega aunque no esté\nsuscrito a nada). Revisa el resultado en `GET /webhooks/deliveries`.\n",
        "x-required-scopes": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Entrega encolada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "delivery"
                  ],
                  "properties": {
                    "error": {
                      "type": "boolean",
                      "const": false
                    },
                    "delivery": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    }
  },
  "webhooks": {
    "ping": {
      "post": {
        "operationId": "webhook_ping",
        "summary": "ping",
        "description": "Respuesta a `POST /webhooks/{id}/test`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventPing"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "order.created": {
      "post": {
        "operationId": "webhook_order_created",
        "summary": "Pedido nuevo",
        "description": "Un pedido de la florería pasa a ser visible para la API (cualquier canal: marketplace,\nsu tienda, venta local). Distingue con `is_internal` y `created_by_api`.\nScope para suscribirse: `orders:read`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventOrder"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "order.status_changed": {
      "post": {
        "operationId": "webhook_order_status_changed",
        "summary": "Cambio de estado de un pedido",
        "description": "Cada cambio de estado es un evento (los pedidos no se agrupan). Incluye\n`previous_status`. Scope para suscribirse: `orders:read`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventOrder"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "order.delivered": {
      "post": {
        "operationId": "webhook_order_delivered",
        "summary": "Pedido entregado",
        "description": "Además de `order.status_changed`. Scope para suscribirse: `orders:read`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventOrder"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "order.cancelled": {
      "post": {
        "operationId": "webhook_order_cancelled",
        "summary": "Pedido cancelado",
        "description": "Además de `order.status_changed`. Scope para suscribirse: `orders:read`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventOrder"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "order.photo_uploaded": {
      "post": {
        "operationId": "webhook_order_photo_uploaded",
        "summary": "Foto de entrega subida",
        "description": "Se envía unos segundos después de subirse la foto, cuando ya existen los tres\ntamaños (`order.photos`). Scope para suscribirse: `orders:read`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventOrder"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "product.created": {
      "post": {
        "operationId": "webhook_product_created",
        "summary": "Producto nuevo",
        "description": "Snapshot completo (forma de `GET /products/{id}`). Scope: `catalog:read`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventProduct"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "product.updated": {
      "post": {
        "operationId": "webhook_product_updated",
        "summary": "Producto modificado",
        "description": "Datos, variantes, imágenes, personalización o cualquier combinación de cambios que no\nsea solo precio o solo estado. Los cambios del mismo producto en una ventana de ~45 s\nllegan juntos, con el estado final. Scope: `catalog:read`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventProduct"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "product.price_changed": {
      "post": {
        "operationId": "webhook_product_price_changed",
        "summary": "Precio modificado",
        "description": "Solo cambió precio, oferta, `sale_date_end` o el precio propio de un producto\ncompartido. Scope: `catalog:read`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventProduct"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "product.status_changed": {
      "post": {
        "operationId": "webhook_product_status_changed",
        "summary": "Producto activado o desactivado",
        "description": "Solo cambió `is_active`. Scope: `catalog:read`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventProduct"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "product.deleted": {
      "post": {
        "operationId": "webhook_product_deleted",
        "summary": "Producto borrado",
        "description": "Payload liviano. También se envía cuando la florería deja de revender un producto.\nScope: `catalog:read`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventProductDeleted"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "product.stock_changed": {
      "post": {
        "operationId": "webhook_product_stock_changed",
        "summary": "Stock modificado (opt-in)",
        "description": "Payload liviano. **No** lo incluyen los comodines: hay que pedirlo explícito al\nregistrar el webhook. Scope: `catalog:read`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventProductStock"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    },
    "availability.changed": {
      "post": {
        "operationId": "webhook_availability_changed",
        "summary": "Cambio de disponibilidad de despacho",
        "description": "Bloqueo de día, alta demanda, comunas u horarios. Un solo evento por ventana aunque\ncambien muchas cosas (no uno por producto): vuelve a consultar\n`/delivery-availability` y `/products/prices`. Scope: `catalog:read`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/HeaderEvent"
          },
          {
            "$ref": "#/components/parameters/HeaderDelivery"
          },
          {
            "$ref": "#/components/parameters/HeaderSignature"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventAvailability"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "$ref": "#/components/responses/WebhookAck"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "rf_live_<prefix>_<secreto>",
        "description": "API key de la florería, con formato `rf_live_<prefix>_<secreto>`. Se entrega una sola\nvez; se envía en cada solicitud como `Authorization: Bearer <key>`.\n"
      }
    },
    "parameters": {
      "Page": {
        "name": "page",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        }
      },
      "PageSize": {
        "name": "page_size",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      },
      "UpdatedSince": {
        "name": "updated_since",
        "in": "query",
        "description": "ISO 8601 o `YYYY-MM-DD`; estricto `>`. Sin zona se interpreta como America/Santiago.\nEnvía siempre ISO 8601 con zona.\n",
        "schema": {
          "type": "string"
        },
        "example": "2026-10-23T12:00:00-03:00"
      },
      "OrderRef": {
        "name": "ref",
        "in": "path",
        "required": true,
        "description": "Id numérico del pedido, o `ext:<external_id>` (solo pedidos creados por API).\n",
        "schema": {
          "type": "string"
        },
        "examples": {
          "id": {
            "value": "70123"
          },
          "external": {
            "value": "ext:WC-10452"
          }
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Texto único de hasta 255 caracteres. Durante 24 h, la misma key con el mismo cuerpo\ndevuelve la misma respuesta sin crear nada; con otro cuerpo, 409\n`idempotency_conflict`. Solo se recuerdan las respuestas 2xx. Un header vacío\nse trata como si no viniera.\n",
        "schema": {
          "type": "string",
          "maxLength": 255
        }
      },
      "HeaderEvent": {
        "name": "X-Redfloral-Event",
        "in": "header",
        "required": true,
        "description": "Nombre del evento.",
        "schema": {
          "type": "string"
        }
      },
      "HeaderDelivery": {
        "name": "X-Redfloral-Delivery",
        "in": "header",
        "required": true,
        "description": "Id único de la entrega. **Deduplica por este valor** (entrega at-least-once).",
        "schema": {
          "type": "string"
        }
      },
      "HeaderSignature": {
        "name": "X-Redfloral-Signature",
        "in": "header",
        "required": true,
        "description": "`t=<unix>,v1=<firma>` con `firma = hex(HMAC-SHA256(secret, \"<t>.<cuerpo crudo>\"))`.\nCalcula sobre los bytes crudos del cuerpo, compara en tiempo constante y rechaza si\n`t` se aleja más de 5 minutos de tu reloj. `secret` es el `whsec_...` del webhook.\n",
        "schema": {
          "type": "string"
        },
        "example": "t=1793400000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
      }
    },
    "headers": {
      "RetryAfter": {
        "description": "Segundos que debes esperar antes de reintentar.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitLimit": {
        "description": "Tamaño del cupo de la ventana (lectura o escritura, según la solicitud).",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitRemaining": {
        "description": "Solicitudes que quedan en la ventana.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "`validation_error`: body o parámetros inválidos (`fields` con las rutas de los campos).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": true,
              "code": "validation_error",
              "message": "Hay campos inválidos.",
              "fields": {
                "products.0.price": "Asegúrese de que este valor es mayor o igual a 0."
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "`invalid_api_key`, `api_key_revoked` o `api_key_expired`.",
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": true,
              "code": "invalid_api_key",
              "message": "API key inválida."
            }
          }
        }
      },
      "Forbidden": {
        "description": "`api_access_disabled` (la florería no tiene la API habilitada), `insufficient_scope`\n(`required_scope` en el cuerpo) u `orders_write_disabled`.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": true,
              "code": "insufficient_scope",
              "message": "La API key no tiene el scope 'orders:write'.",
              "required_scope": "orders:write"
            }
          }
        }
      },
      "NotFound": {
        "description": "`not_found`: no existe o es de otra florería.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": true,
              "code": "not_found",
              "message": "Recurso no encontrado."
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "`rate_limited`: superaste el límite de la key.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": true,
              "code": "rate_limited",
              "message": "Superaste el límite de solicitudes. Reintenta en 30 segundos.",
              "retry_after": 30
            }
          }
        }
      },
      "WebhookAck": {
        "description": "Cualquier 2xx confirma la recepción. Cualquier otra respuesta, un redirect o un timeout (10 s) cuenta como fallo y se reintenta."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "code",
          "message"
        ],
        "description": "Todo error. Programa contra `code`; `message` puede cambiar de redacción. Algunos\nerrores agregan campos (`required_scope`, `retry_after`, `pending_count`,\n`pending_limit`, `status`, `order_id`).\n",
        "properties": {
          "error": {
            "type": "boolean",
            "const": true
          },
          "code": {
            "type": "string",
            "enum": [
              "invalid_api_key",
              "api_key_revoked",
              "api_key_expired",
              "api_access_disabled",
              "insufficient_scope",
              "orders_write_disabled",
              "validation_error",
              "not_found",
              "method_not_allowed",
              "unsupported_media_type",
              "rate_limited",
              "internal_orders_blocked",
              "order_not_cancellable",
              "service_unavailable",
              "webhook_limit_reached",
              "idempotency_conflict",
              "duplicate_document",
              "error"
            ]
          },
          "message": {
            "type": "string"
          },
          "fields": {
            "type": "object",
            "description": "Rutas de los campos inválidos, p. ej. `address.zone_id` o `products.0.name`.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "required_scope": {
            "type": "string"
          },
          "retry_after": {
            "type": "integer"
          },
          "pending_count": {
            "type": "integer"
          },
          "pending_limit": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "order_id": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      },
      "ListEnvelope": {
        "type": "object",
        "required": [
          "error",
          "data",
          "count",
          "has_more",
          "page",
          "page_size"
        ],
        "properties": {
          "error": {
            "type": "boolean",
            "const": false
          },
          "data": {
            "type": "array",
            "items": {}
          },
          "count": {
            "type": "integer",
            "description": "Total de ítems (no solo de la página)."
          },
          "has_more": {
            "type": "boolean"
          },
          "page": {
            "type": "integer"
          },
          "page_size": {
            "type": "integer"
          }
        }
      },
      "Ping": {
        "type": "object",
        "required": [
          "error",
          "company_id",
          "company_name",
          "key",
          "scopes",
          "server_time"
        ],
        "properties": {
          "error": {
            "type": "boolean",
            "const": false
          },
          "company_id": {
            "type": "integer"
          },
          "company_name": {
            "type": "string"
          },
          "key": {
            "type": "object",
            "required": [
              "name",
              "prefix"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "prefix": {
                "type": "string"
              }
            }
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            }
          },
          "server_time": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Scope": {
        "type": "string",
        "enum": [
          "orders:read",
          "orders:write",
          "catalog:read",
          "catalog:write",
          "availability:write",
          "orders:status",
          "billing:read"
        ]
      },
      "Me": {
        "type": "object",
        "required": [
          "error",
          "company_id",
          "plan",
          "api_access",
          "orders_write_enabled",
          "free_orders_remaining",
          "next_order_cost",
          "pending_count",
          "pending_limit",
          "is_blocked"
        ],
        "properties": {
          "error": {
            "type": "boolean",
            "const": false
          },
          "company_id": {
            "type": "integer"
          },
          "plan": {
            "type": "string"
          },
          "api_access": {
            "type": "boolean"
          },
          "orders_write_enabled": {
            "type": "boolean"
          },
          "free_orders_remaining": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Ventas locales gratis que quedan en el cupo del plan."
          },
          "next_order_cost": {
            "type": [
              "number",
              "string",
              "null"
            ],
            "description": "Costo del próximo pedido (0 si queda cupo)."
          },
          "pending_count": {
            "type": "integer",
            "description": "Pedidos con excedente sin liquidar."
          },
          "pending_limit": {
            "type": [
              "integer",
              "null"
            ]
          },
          "is_blocked": {
            "type": "boolean"
          }
        }
      },
      "Zone": {
        "type": "object",
        "required": [
          "id",
          "name",
          "name_cleaned",
          "price",
          "time_end",
          "is_retirement"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "description": "Es el `zone_id` del `POST /orders`."
          },
          "name": {
            "type": "string"
          },
          "name_cleaned": {
            "type": "string",
            "description": "Minúsculas y sin acentos."
          },
          "price": {
            "type": "integer"
          },
          "time_end": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hora de corte (`HH:MM`)."
          },
          "is_retirement": {
            "type": "boolean"
          }
        }
      },
      "Schedule": {
        "type": "object",
        "required": [
          "id",
          "day",
          "type_day",
          "time_start",
          "time_end",
          "time_close",
          "price"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "description": "Es el `shipping_schedule_id` del `POST /orders`."
          },
          "day": {
            "type": "integer",
            "minimum": 0,
            "maximum": 6,
            "description": "0 = lunes."
          },
          "type_day": {
            "type": "string",
            "enum": [
              "DI",
              "AN"
            ],
            "description": "DI mismo día · AN anticipado."
          },
          "time_start": {
            "type": "string"
          },
          "time_end": {
            "type": "string"
          },
          "time_close": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "type": "integer"
          }
        }
      },
      "AvailabilityReason": {
        "type": "string",
        "description": "Motivo por el que no hay disponibilidad (`null` si la hay).",
        "enum": [
          "day_blocked",
          "not_working_day",
          "zone_closed_for_today",
          "zone_inactive",
          "no_schedules",
          "product_excluded_high_demand",
          "product_inactive",
          "date_in_past",
          "zone_not_matched"
        ]
      },
      "DeliveryAvailability": {
        "type": "object",
        "required": [
          "error",
          "date",
          "zone_id",
          "zone_name",
          "available",
          "reason",
          "schedules",
          "high_demand",
          "shipping_cost_base"
        ],
        "properties": {
          "error": {
            "type": "boolean",
            "const": false
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "zone_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "zone_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "available": {
            "type": "boolean"
          },
          "reason": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AvailabilityReason"
              },
              {
                "type": "null"
              }
            ]
          },
          "schedules": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "time_start",
                "time_end",
                "price"
              ],
              "properties": {
                "id": {
                  "type": "integer",
                  "description": "Es el `shipping_schedule_id`."
                },
                "time_start": {
                  "type": "string"
                },
                "time_end": {
                  "type": "string"
                },
                "price": {
                  "type": "integer",
                  "description": "Comuna + horario: lo que va en `shipping_cost`."
                }
              }
            }
          },
          "high_demand": {
            "type": "boolean"
          },
          "shipping_cost_base": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Precio de la comuna."
          }
        }
      },
      "Product": {
        "type": "object",
        "required": [
          "id",
          "shared",
          "name",
          "description",
          "type",
          "price",
          "price_sale",
          "sale_date_end",
          "is_active",
          "is_stock",
          "quantity",
          "is_exclude_high_demand",
          "photo",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "description": "Id del producto original; es el `product_id` del `POST /orders`."
          },
          "shared": {
            "type": "boolean",
            "description": "La florería lo revende (precio, oferta, stock y estado del compartido; o los del producto original si la reventa está configurada para usarlos, y entonces `is_active` también exige el original activo y con stock)."
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "type": {
            "type": "integer",
            "enum": [
              0,
              1,
              2,
              3
            ],
            "description": "0 normal · 1 adicional · 2 por unidad de flores · 3 con variantes."
          },
          "price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "price_sale": {
            "type": [
              "integer",
              "null"
            ]
          },
          "sale_date_end": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "is_active": {
            "type": "boolean"
          },
          "is_stock": {
            "type": "boolean"
          },
          "quantity": {
            "type": [
              "integer",
              "null"
            ]
          },
          "is_exclude_high_demand": {
            "type": "boolean"
          },
          "photo": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProductDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Product"
          },
          {
            "type": "object",
            "required": [
              "is_message",
              "message_max",
              "message_label",
              "personalized_min",
              "personalized_max",
              "personalized_max_combinations",
              "images",
              "variants",
              "options"
            ],
            "properties": {
              "is_message": {
                "type": "boolean"
              },
              "message_max": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "message_label": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "personalized_min": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "personalized_max": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "personalized_max_combinations": {
                "type": "integer",
                "description": "Máximo de combinaciones de un producto por unidad (`type` 2), por defecto 2. `POST /orders` no lo valida."
              },
              "images": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "url",
                    "small",
                    "medium",
                    "large"
                  ],
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "url": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "small": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "medium": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "large": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              },
              "variants": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "sku",
                    "default",
                    "price",
                    "stock",
                    "photo",
                    "attributes"
                  ],
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "sku": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "default": {
                      "type": "boolean"
                    },
                    "price": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "stock": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "photo": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "attributes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "attribute",
                          "value"
                        ],
                        "properties": {
                          "attribute": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "options": {
                "description": "Opciones disponibles de un producto tipo 2.",
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "name",
                    "price",
                    "photo"
                  ],
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "price": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "photo": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "ProductPrice": {
        "type": "object",
        "required": [
          "id",
          "shared",
          "type",
          "price",
          "price_sale",
          "final_price",
          "high_demand_percentage"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "shared": {
            "type": "boolean"
          },
          "type": {
            "type": "integer",
            "enum": [
              0,
              1,
              2,
              3
            ]
          },
          "price": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Precio normal con alta demanda."
          },
          "price_sale": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Oferta vigente ese día con alta demanda, o null."
          },
          "final_price": {
            "type": [
              "integer",
              "null"
            ]
          },
          "high_demand_percentage": {
            "type": "integer"
          },
          "variants": {
            "type": "array",
            "description": "Solo en tipo 3.",
            "items": {
              "type": "object",
              "required": [
                "id",
                "price"
              ],
              "properties": {
                "id": {
                  "type": "integer"
                },
                "price": {
                  "type": [
                    "integer",
                    "null"
                  ]
                }
              }
            }
          },
          "options": {
            "type": "array",
            "description": "Solo en tipo 2.",
            "items": {
              "type": "object",
              "required": [
                "id",
                "price"
              ],
              "properties": {
                "id": {
                  "type": "integer"
                },
                "price": {
                  "type": [
                    "integer",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "WarningCode": {
        "type": "string",
        "enum": [
          "zone_not_matched",
          "schedule_not_matched",
          "date_not_available",
          "options_stock_insufficient",
          "variant_stock_insufficient"
        ]
      },
      "Warning": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "description": "Aviso, no error. `zone_not_matched` y `schedule_not_matched` se recalculan en cada\nlectura mientras falte la zona/horario; `date_not_available`,\n`options_stock_insufficient` y `variant_stock_insufficient` solo vienen en la\nrespuesta del `POST`.\n",
        "properties": {
          "code": {
            "$ref": "#/components/schemas/WarningCode"
          },
          "message": {
            "type": "string"
          },
          "reason": {
            "description": "Solo en `date_not_available`.",
            "type": "string",
            "enum": [
              "day_blocked",
              "not_working_day",
              "zone_closed_for_today",
              "zone_inactive",
              "no_schedules",
              "product_excluded_high_demand",
              "product_inactive",
              "date_in_past",
              "schedule_not_available"
            ]
          }
        }
      },
      "OrderStatus": {
        "type": "object",
        "required": [
          "slug",
          "name"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "rejected",
              "in_transit",
              "delivered",
              "problem",
              "cancelled"
            ]
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Order": {
        "type": "object",
        "required": [
          "id",
          "external_id",
          "external_url",
          "status",
          "is_internal",
          "created_by_api",
          "secure_key",
          "status_url",
          "shipping_date",
          "schedule_id",
          "schedule_text",
          "zone_id",
          "zone_text",
          "customer",
          "address",
          "card_message",
          "anonymous",
          "total_products",
          "total_shipping",
          "total_discount",
          "total",
          "delivered_at",
          "created_at",
          "updated_at",
          "warnings"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "external_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/OrderStatus"
          },
          "is_internal": {
            "type": "boolean",
            "description": "Venta local."
          },
          "created_by_api": {
            "type": "boolean"
          },
          "secure_key": {
            "type": "string",
            "format": "uuid"
          },
          "status_url": {
            "type": "string",
            "format": "uri",
            "description": "Página pública de seguimiento: puedes enviarla a tu cliente."
          },
          "shipping_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "schedule_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Id de `CompanySchedule` (no es el `shipping_schedule_id`)."
          },
          "schedule_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "zone_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "zone_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer": {
            "type": "object",
            "required": [
              "name",
              "email",
              "phone_code",
              "phone"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "email": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "phone_code": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "phone": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "address": {
            "type": "object",
            "required": [
              "type",
              "name",
              "street",
              "street_number",
              "more_info",
              "phone",
              "latitude",
              "longitude"
            ],
            "properties": {
              "type": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "street": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "street_number": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "more_info": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "phone": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "latitude": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "longitude": {
                "type": [
                  "number",
                  "null"
                ]
              }
            }
          },
          "card_message": {
            "type": "string"
          },
          "anonymous": {
            "type": "boolean",
            "description": "Envío anónimo: el destinatario no ve quién envía."
          },
          "total_products": {
            "type": "integer"
          },
          "total_shipping": {
            "type": "integer"
          },
          "total_discount": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Warning"
            }
          }
        }
      },
      "OrderDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Order"
          },
          {
            "type": "object",
            "required": [
              "url_photo",
              "photos",
              "products",
              "history",
              "document_type",
              "document"
            ],
            "properties": {
              "url_photo": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri",
                "description": "Página pública de la foto de entrega."
              },
              "photos": {
                "description": "URL públicas de la foto de entrega, o null si aún no hay foto.",
                "oneOf": [
                  {
                    "type": "object",
                    "required": [
                      "xs",
                      "md",
                      "lg"
                    ],
                    "properties": {
                      "xs": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "uri"
                      },
                      "md": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "uri"
                      },
                      "lg": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "uri"
                      }
                    }
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "products": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "product_id",
                    "variant_id",
                    "shared",
                    "name",
                    "quantity",
                    "price",
                    "note",
                    "options"
                  ],
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "product_id": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "variant_id": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "shared": {
                      "type": "boolean"
                    },
                    "name": {
                      "type": "string"
                    },
                    "quantity": {
                      "type": "integer"
                    },
                    "price": {
                      "type": "integer"
                    },
                    "note": {
                      "type": "string"
                    },
                    "options": {
                      "description": "Opciones vendidas de un producto por unidad (`type` 2). Lista vacía en otros tipos y en pedidos antiguos.",
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name",
                          "quantity",
                          "price"
                        ],
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "quantity": {
                            "type": "integer"
                          },
                          "price": {
                            "type": "integer",
                            "description": "Precio unitario de catálogo de la opción para la fecha de despacho, alta demanda incluida."
                          }
                        }
                      }
                    }
                  }
                }
              },
              "history": {
                "description": "Más nuevo primero.",
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "status",
                    "status_name",
                    "message",
                    "created_at"
                  ],
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "status_name": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              },
              "document_type": {
                "type": "string",
                "enum": [
                  "boleta",
                  "factura"
                ],
                "description": "Documento tributario del pedido. En el marketplace lo elige el cliente; en un pedido creado por API es el `document_type` del `POST /orders` (por defecto `boleta`)."
              },
              "document": {
                "description": "Boleta o factura subida (`POST /orders/{ref}/document` o el panel), o null si aún no hay.",
                "oneOf": [
                  {
                    "type": "object",
                    "required": [
                      "number",
                      "date",
                      "file_url",
                      "page_url",
                      "validation"
                    ],
                    "properties": {
                      "number": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Folio."
                      },
                      "date": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "date"
                      },
                      "file_url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "page_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Página pública del documento en Redfloral."
                      },
                      "validation": {
                        "type": "string",
                        "enum": [
                          "pending",
                          "valid",
                          "partially_valid",
                          "invalid",
                          "unreadable",
                          "error",
                          "unsupported_format"
                        ],
                        "description": "Revisión con IA de folio, monto y RUT emisor. `pending` mientras corre;\n`partially_valid` = coincide el folio o el monto, no ambos.\n"
                      }
                    }
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            }
          }
        ]
      },
      "OrderResponse": {
        "type": "object",
        "required": [
          "error",
          "created",
          "order"
        ],
        "properties": {
          "error": {
            "type": "boolean",
            "const": false
          },
          "created": {
            "type": "boolean"
          },
          "order": {
            "$ref": "#/components/schemas/OrderDetail"
          }
        }
      },
      "OrderCreate": {
        "type": "object",
        "required": [
          "external_id",
          "customer",
          "address",
          "shipping_date",
          "shipping_cost",
          "products"
        ],
        "description": "Hay que indicar `shipping_schedule_id` **o** `schedule_text` (el id manda). Los campos\ndesconocidos se ignoran.\n",
        "properties": {
          "external_id": {
            "type": "string",
            "maxLength": 64,
            "description": "Tu número de pedido; único por florería."
          },
          "external_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "maxLength": 200
          },
          "customer": {
            "type": "object",
            "required": [
              "name",
              "email",
              "phone"
            ],
            "properties": {
              "name": {
                "type": "string",
                "maxLength": 200
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "phone": {
                "type": "string",
                "maxLength": 32,
                "description": "Se acepta con el código pegado (\"+56 9 1234 5678\")."
              },
              "phone_code": {
                "type": "string",
                "maxLength": 8,
                "default": "56"
              }
            }
          },
          "address": {
            "type": "object",
            "required": [
              "zone_text",
              "name",
              "street",
              "street_number"
            ],
            "properties": {
              "zone_text": {
                "type": "string",
                "maxLength": 64,
                "description": "Comuna tal como la tiene el socio; match exacto sin importar mayúsculas ni acentos."
              },
              "zone_id": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Id de `GET /zones`; si viene, manda sobre `zone_text`."
              },
              "type": {
                "type": "string",
                "maxLength": 20,
                "default": "DrParticular"
              },
              "name": {
                "type": "string",
                "maxLength": 128,
                "description": "Nombre del destinatario."
              },
              "street": {
                "type": "string",
                "maxLength": 128
              },
              "street_number": {
                "type": "string",
                "maxLength": 32
              },
              "more_info": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 240
              },
              "phone": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 96
              },
              "latitude": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "longitude": {
                "type": [
                  "number",
                  "null"
                ]
              }
            }
          },
          "shipping_date": {
            "type": "string",
            "format": "date"
          },
          "shipping_schedule_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Id de `GET /schedules` o de `delivery-availability`."
          },
          "schedule_text": {
            "type": "string",
            "maxLength": 64,
            "description": "Respaldo del id; solo se resuelve si es exacto `HH:MM-HH:MM` de un horario activo."
          },
          "shipping_cost": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2147483647,
            "description": "Obligatorio. `0` es válido (envío gratis); ausente, `null` o negativo da 400."
          },
          "discount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2147483647,
            "default": 0,
            "description": "Opcional. Descuento total del pedido, en monto (sin cupones), que ya le aplicaste a tu cliente. Se resta de los productos, no del despacho, y vuelve como `total_discount`. Mayor que `total_products` (ya calculado por el servidor) o negativo da 400 `fields.discount`. Manda el precio de lista en cada línea y el descuento acá."
          },
          "card_message": {
            "type": [
              "string",
              "null"
            ]
          },
          "anonymous": {
            "type": "boolean",
            "default": false,
            "description": "Opcional. `true` = envío anónimo: el pedido se marca para que la florería no revele quién envía. Vuelve en la respuesta como `anonymous`."
          },
          "document_type": {
            "type": "string",
            "enum": [
              "boleta",
              "factura"
            ],
            "default": "boleta",
            "description": "Opcional. Documento tributario que emites por el pedido. Vuelve en el detalle como `document_type`; se puede corregir al subir el documento (`POST /orders/{ref}/document`)."
          },
          "products": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "type": "object",
              "required": [
                "quantity"
              ],
              "description": "Sin `product_id`, `name` es obligatorio (producto libre). `price` es obligatorio salvo en `type` 2 y 3.",
              "properties": {
                "product_id": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "variant_id": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Solo junto a `product_id`. Obligatorio en un producto con variantes (`type` 3): 400 `products.N.variant_id` si falta, si no es de ese producto o no tiene precio. Si no alcanza el stock (se suma todo el pedido) el pedido se crea igual, con el aviso `variant_stock_insufficient`, y cancelar no devuelve esas líneas. No se acepta en un producto que la florería revende (`shared: true`): 400."
                },
                "options": {
                  "type": [
                    "array",
                    "null"
                  ],
                  "maxItems": 100,
                  "description": "Obligatorio en un producto por unidad (`type` 2) y solo aceptado ahí: 400\n`products.N.options` si falta, si una opción no es de ese producto o está\ninactiva, o si el total de unidades queda fuera de\n`personalized_min`/`personalized_max`. Si no alcanza el stock (se suma todo el\npedido) el pedido se crea igual, con el aviso `options_stock_insufficient`.\nDescuenta el stock de las opciones al crear el pedido y lo devuelve al\ncancelar, salvo el de las opciones aceptadas sin stock suficiente. El `price` de la línea lo calcula Redfloral de las opciones, con el\nrecargo de alta demanda de `shipping_date`.\n",
                  "items": {
                    "type": "object",
                    "required": [
                      "id",
                      "quantity"
                    ],
                    "properties": {
                      "id": {
                        "type": "integer",
                        "description": "Id de la opción (`options` de `GET /products/<id>` o `/products/prices`). Sin repetir."
                      },
                      "quantity": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 32767,
                        "description": "Unidades de la opción por cada unidad de la línea."
                      }
                    }
                  }
                },
                "name": {
                  "type": "string",
                  "maxLength": 200
                },
                "quantity": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 32767
                },
                "price": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 2147483647,
                  "description": "Lo que cobró el socio; no se reemplaza por el del catálogo. Obligatorio, salvo en un producto por unidad (`type` 2) o con variantes (`type` 3): ahí se ignora y se calcula de `options` o de la variante, con el recargo de alta demanda de `shipping_date`."
                },
                "note": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "DeliveryStatus": {
        "type": "string",
        "enum": [
          "pending",
          "delivered",
          "failed",
          "abandoned"
        ]
      },
      "EventName": {
        "type": "string",
        "enum": [
          "order.created",
          "order.status_changed",
          "order.delivered",
          "order.cancelled",
          "order.photo_uploaded",
          "product.created",
          "product.updated",
          "product.deleted",
          "product.status_changed",
          "product.price_changed",
          "product.stock_changed",
          "availability.changed"
        ]
      },
      "Webhook": {
        "type": "object",
        "required": [
          "id",
          "name",
          "url",
          "events",
          "is_active",
          "consecutive_failures",
          "disabled_by_failures",
          "disabled_reason",
          "disabled_at",
          "last_delivery_at",
          "created_at",
          "updated_at",
          "secret"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventName"
            }
          },
          "is_active": {
            "type": "boolean"
          },
          "consecutive_failures": {
            "type": "integer"
          },
          "disabled_by_failures": {
            "type": "boolean",
            "description": "`true` si el webhook se desactivó solo tras fallos consecutivos (con `disabled_reason` y `disabled_at`); `false` si está activo o lo pausaste a mano."
          },
          "disabled_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "disabled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_delivery_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "secret": {
            "type": "string",
            "description": "`whsec_...`; la clave HMAC de la firma."
          }
        }
      },
      "WebhookResponse": {
        "type": "object",
        "required": [
          "error",
          "webhook"
        ],
        "properties": {
          "error": {
            "type": "boolean",
            "const": false
          },
          "webhook": {
            "$ref": "#/components/schemas/Webhook"
          }
        }
      },
      "WebhookCreate": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 500,
            "description": "https y pública."
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "description": "Un evento o un comodín (`*`, `order.*`, `product.*`, `availability.*`)."
            }
          }
        }
      },
      "WebhookUpdate": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "maxLength": 500
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string"
            }
          },
          "is_active": {
            "type": "boolean"
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "required": [
          "id",
          "webhook_id",
          "event",
          "status",
          "attempts",
          "status_code",
          "last_error",
          "order_id",
          "product_id",
          "next_attempt_at",
          "delivered_at",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "webhook_id": {
            "type": "integer"
          },
          "event": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/DeliveryStatus"
          },
          "attempts": {
            "type": "integer"
          },
          "status_code": {
            "type": [
              "integer",
              "null"
            ]
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "order_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "product_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "next_attempt_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "payload": {
            "type": "object",
            "description": "Solo con `include_payload=true`."
          }
        }
      },
      "EventBase": {
        "type": "object",
        "required": [
          "event",
          "occurred_at",
          "company_id",
          "delivery_id"
        ],
        "properties": {
          "event": {
            "$ref": "#/components/schemas/EventName"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "company_id": {
            "type": "integer"
          },
          "delivery_id": {
            "type": "integer",
            "description": "Igual a `X-Redfloral-Delivery`."
          }
        }
      },
      "EventPing": {
        "type": "object",
        "required": [
          "event",
          "occurred_at",
          "company_id",
          "delivery_id",
          "webhook_id",
          "message"
        ],
        "properties": {
          "event": {
            "type": "string",
            "const": "ping"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "company_id": {
            "type": "integer"
          },
          "delivery_id": {
            "type": "integer"
          },
          "webhook_id": {
            "type": "integer"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "EventOrder": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EventBase"
          },
          {
            "type": "object",
            "required": [
              "order"
            ],
            "properties": {
              "order": {
                "$ref": "#/components/schemas/OrderDetail"
              },
              "previous_status": {
                "description": "Slug del estado anterior (en `order.status_changed`, `order.delivered` y `order.cancelled`).",
                "type": "string"
              }
            }
          }
        ],
        "example": {
          "event": "order.status_changed",
          "occurred_at": "2026-11-02T15:04:05.123456+00:00",
          "company_id": 15,
          "delivery_id": 9001,
          "previous_status": "processing",
          "order": {
            "id": 70123,
            "external_id": "WC-10452",
            "external_url": "https://mitienda.cl/wp-admin/post.php?post=10452",
            "status": {
              "slug": "processing",
              "name": "Aceptado"
            },
            "is_internal": true,
            "created_by_api": true,
            "secure_key": "6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
            "status_url": "https://redfloral.cl/estado-del-pedido/6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
            "shipping_date": "2026-11-02",
            "schedule_id": 31,
            "schedule_text": "Entre 10:00 y 14:00 hrs",
            "zone_id": 12,
            "zone_text": "Ñuñoa",
            "customer": {
              "name": "Ana Pérez",
              "email": "ana@correo.cl",
              "phone_code": "56",
              "phone": "912345678"
            },
            "address": {
              "type": "DrParticular",
              "name": "María González",
              "street": "Los Aromos",
              "street_number": "1234",
              "more_info": "Depto 501",
              "phone": "987654321",
              "latitude": -33.41,
              "longitude": -70.6
            },
            "card_message": "Feliz cumpleaños",
            "anonymous": false,
            "total_products": 35980,
            "total_shipping": 3990,
            "total_discount": 0,
            "total": 39970,
            "delivered_at": null,
            "created_at": "2026-10-23T15:04:05Z",
            "updated_at": "2026-10-23T15:04:05Z",
            "warnings": [],
            "url_photo": "https://redfloral.cl/foto-de-entrega/6b3f3f0a-2c1e-4b6e-9f55-0d7b1a2c3d4e",
            "photos": null,
            "products": [
              {
                "id": 1,
                "product_id": 8891,
                "variant_id": null,
                "shared": false,
                "name": "Ramo Primavera",
                "quantity": 1,
                "price": 29990,
                "note": "",
                "options": []
              },
              {
                "id": 2,
                "product_id": null,
                "variant_id": null,
                "shared": false,
                "name": "Globo personalizado",
                "quantity": 1,
                "price": 5990,
                "note": "",
                "options": []
              }
            ],
            "history": [
              {
                "status": "processing",
                "status_name": "Aceptado",
                "message": "Pedido creado por API",
                "created_at": "2026-10-23T15:04:05Z"
              }
            ],
            "document_type": "boleta",
            "document": null
          }
        }
      },
      "ProductChange": {
        "type": "string",
        "enum": [
          "created",
          "data",
          "variants",
          "images",
          "personalization",
          "price",
          "status",
          "stock"
        ]
      },
      "EventProduct": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EventBase"
          },
          {
            "type": "object",
            "required": [
              "product",
              "changes"
            ],
            "properties": {
              "product": {
                "$ref": "#/components/schemas/ProductDetail"
              },
              "changes": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ProductChange"
                }
              }
            }
          }
        ],
        "example": {
          "event": "product.price_changed",
          "occurred_at": "2026-11-02T15:04:50+00:00",
          "company_id": 15,
          "delivery_id": 9002,
          "changes": [
            "price"
          ],
          "product": {
            "id": 8891,
            "shared": false,
            "name": "Ramo Primavera",
            "description": "Rosas y lirios.",
            "type": 0,
            "price": 25000,
            "price_sale": null,
            "sale_date_end": null,
            "is_active": true,
            "is_stock": true,
            "quantity": 7,
            "is_exclude_high_demand": false,
            "photo": null,
            "updated_at": "2026-11-02T15:04:49Z",
            "is_message": false,
            "message_max": null,
            "message_label": null,
            "personalized_min": null,
            "personalized_max": null,
            "personalized_max_combinations": 2,
            "images": [],
            "variants": [],
            "options": []
          }
        }
      },
      "EventProductDeleted": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EventBase"
          },
          {
            "type": "object",
            "required": [
              "product_id"
            ],
            "properties": {
              "product_id": {
                "type": "integer"
              }
            }
          }
        ],
        "example": {
          "event": "product.deleted",
          "occurred_at": "2026-11-02T15:10:00+00:00",
          "company_id": 15,
          "delivery_id": 9003,
          "product_id": 8891
        }
      },
      "EventProductStock": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EventBase"
          },
          {
            "type": "object",
            "required": [
              "product_id",
              "is_stock",
              "quantity",
              "variants"
            ],
            "properties": {
              "product_id": {
                "type": "integer"
              },
              "is_stock": {
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "quantity": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "variants": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "stock"
                  ],
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "stock": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        ],
        "example": {
          "event": "product.stock_changed",
          "occurred_at": "2026-11-02T15:12:00+00:00",
          "company_id": 15,
          "delivery_id": 9004,
          "product_id": 8891,
          "is_stock": true,
          "quantity": 7,
          "variants": [
            {
              "id": 51,
              "stock": 3
            }
          ]
        }
      },
      "AvailabilityChange": {
        "type": "object",
        "required": [
          "type",
          "action"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "day_block",
              "high_demand",
              "zone",
              "schedule"
            ]
          },
          "action": {
            "type": "string",
            "enum": [
              "created",
              "updated",
              "deleted"
            ]
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "En `day_block` y `high_demand`."
          },
          "percentage": {
            "type": "integer",
            "description": "Solo en `high_demand`."
          },
          "is_active": {
            "type": "boolean",
            "description": "En `high_demand`, `zone` y `schedule`."
          },
          "zone_id": {
            "type": "integer",
            "description": "Solo en `zone` (id de `GET /zones`)."
          },
          "schedule_id": {
            "type": "integer",
            "description": "Solo en `schedule` (id de `GET /schedules`)."
          },
          "day": {
            "type": "integer",
            "description": "Solo en `schedule` (0 = lunes)."
          }
        }
      },
      "EventAvailability": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EventBase"
          },
          {
            "type": "object",
            "required": [
              "changes"
            ],
            "properties": {
              "changes": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AvailabilityChange"
                }
              }
            }
          }
        ],
        "example": {
          "event": "availability.changed",
          "occurred_at": "2026-11-02T15:15:00+00:00",
          "company_id": 15,
          "delivery_id": 9005,
          "changes": [
            {
              "type": "day_block",
              "action": "created",
              "date": "2026-11-14"
            },
            {
              "type": "high_demand",
              "action": "updated",
              "date": "2026-11-20",
              "percentage": 20,
              "is_active": true
            },
            {
              "type": "zone",
              "action": "updated",
              "zone_id": 12,
              "is_active": true
            },
            {
              "type": "schedule",
              "action": "updated",
              "schedule_id": 7,
              "day": 4,
              "is_active": true
            }
          ]
        }
      }
    }
  }
}
