{
  "openapi": "3.1.0",
  "info": {
    "title": "DZBuild API for apps",
    "version": "1.8",
    "description": "The DZBuild API as an installed app sees it: every operation an install token can call, with the scope each one needs. An app registers in the developer console at https://dzbuild.com/dashboard/developer, installs on a merchant's store through OAuth 2.0 with PKCE and receives one install token per store. Operations reserved for merchant API keys and assistant connections are left out.",
    "termsOfService": "https://dzbuild.com/terms",
    "contact": {
      "email": "support@dzbuild.com"
    }
  },
  "servers": [
    {
      "url": "https://api.dzbuild.app"
    }
  ],
  "tags": [
    {
      "name": "Analytics",
      "description": "Store sales and traffic figures for a date range."
    },
    {
      "name": "Categories",
      "description": "Product categories, their hierarchy and their order."
    },
    {
      "name": "Connection",
      "description": "Check that the API answers and describe the install token that is calling."
    },
    {
      "name": "Customers",
      "description": "Customers of the store and each customer's orders."
    },
    {
      "name": "Landing pages",
      "description": "Landing pages, their sections, publish checks and AI generation."
    },
    {
      "name": "Orders",
      "description": "Orders: list, read, create, change status, cancel and hand to the courier."
    },
    {
      "name": "Pixels",
      "description": "Advertising tracking pixels installed on the storefront."
    },
    {
      "name": "Products",
      "description": "Products with their images, variants, input fields, offers, quantity rules and stock."
    },
    {
      "name": "Promo codes",
      "description": "Discount codes buyers enter at checkout."
    },
    {
      "name": "Shipping",
      "description": "Delivery rates, settings, couriers, coverage and the wilaya and commune lists."
    },
    {
      "name": "Store",
      "description": "Store profile, design, home page sections, themes and a health check."
    },
    {
      "name": "WhatsApp",
      "description": "Platform-approved WhatsApp order messages to buyers, paid from the store's WhatsApp wallet."
    }
  ],
  "paths": {
    "/v1/analytics": {
      "get": {
        "operationId": "getAnalytics",
        "tags": [
          "Analytics"
        ],
        "summary": "Get store analytics for a period",
        "description": "Returns the same figures as the dashboard analytics page for the chosen period: KPIs with the change against the previous period of the same length, and chart series for orders, revenue, visitors, devices, traffic sources, top products and top wilayas. Results are cached for 120 seconds per store and period. Revenue, profit and average order value, and the revenue values in the charts, are removed unless the key belongs to the store owner. Requires `analytics:read`, which new merchant keys and assistant connections carry; a key minted before the scope was added to the defaults gets 403 `forbidden`.",
        "security": [
          {
            "dzOAuth": [
              "analytics:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "name": "range",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "today",
                "yesterday",
                "7d",
                "30d",
                "this_month",
                "last_month",
                "this_year",
                "custom"
              ],
              "default": "this_month"
            },
            "description": "Preset period. custom needs from and to."
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "YYYY-MM-DD, for range=custom."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "YYYY-MM-DD, for range=custom. At most 365 days after from, never before it."
          }
        ],
        "responses": {
          "200": {
            "description": "The analytics report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AnalyticsReport"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/categories": {
      "get": {
        "operationId": "listCategories",
        "tags": [
          "Categories"
        ],
        "summary": "List categories",
        "description": "Returns the store's categories, newest first, as a cursor list (`items`, `next_cursor`, `has_more`). Each item carries its product count. Filter with `status` and `parent_id`; an unknown `status` value is ignored.",
        "security": [
          {
            "dzOAuth": [
              "products:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "inactive"
              ]
            },
            "description": "Only categories with this status."
          },
          {
            "name": "parent_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^([0-9]+|null)?$"
            },
            "description": "A category id returns its subcategories. `0`, `null` or an empty value returns top-level categories only. Anything else returns 400."
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of categories.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "items",
                        "next_cursor",
                        "has_more"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/CategoryListItem"
                          }
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Pass as `cursor` to fetch the next page; null on the last page."
                        },
                        "has_more": {
                          "type": "boolean"
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createCategory",
        "tags": [
          "Categories"
        ],
        "summary": "Create a category",
        "description": "Creates a category at the end of the store's sort order and returns it with 201. The slug is generated from the name and made unique. A category image can only be added in the dashboard. The change is recorded in the change history and can be reverted with `POST /v1/changes/{id}/undo`. Every write requires an `Idempotency-Key` header; re-sending the same key with a different body returns 422 `idempotency_key_reuse`. Unprocessable codes: `validation_error` (missing or too long name, bad status, non-integer parent_id), `invalid_parent` (parent not in this store, parent is itself a subcategory), `idempotency_key_reuse`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created category.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Category"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/categories/reorder": {
      "post": {
        "operationId": "reorderCategories",
        "tags": [
          "Categories"
        ],
        "summary": "Reorder categories",
        "description": "Sets sort_order for the listed categories in one transaction: either all are updated or none. Array position is the new order unless an item carries its own sort_order. This change is not recorded in the change history, so it cannot be undone. Every write requires an `Idempotency-Key` header; re-sending the same key with a different body returns 422 `idempotency_key_reuse`. A missing or non-array `categories` returns 400; an id outside this store returns 404 `not_found`. Unprocessable codes: `validation_error` (empty array, non-positive or repeated id, non-integer sort_order), `idempotency_key_reuse`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryReorderInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The applied order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CategoryReorderResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/categories/{id}": {
      "get": {
        "operationId": "getCategory",
        "tags": [
          "Categories"
        ],
        "summary": "Get a category",
        "description": "Returns one category with its subcategories (`children`) and its product count. A non-numeric id returns 400.",
        "security": [
          {
            "dzOAuth": [
              "products:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The category.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CategoryDetail"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateCategory",
        "tags": [
          "Categories"
        ],
        "summary": "Update a category",
        "description": "Changes only the fields you send. Renaming regenerates the slug. Categories stay two levels deep: the new parent must be a top-level category of this store, and a category with subcategories cannot be moved under another one. An empty body changes nothing and returns the category. The change is recorded in the change history and can be reverted with `POST /v1/changes/{id}/undo`. Every write requires an `Idempotency-Key` header; re-sending the same key with a different body returns 422 `idempotency_key_reuse`. Unprocessable codes: `validation_error`, `invalid_parent`, `idempotency_key_reuse`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated category.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Category"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteCategory",
        "tags": [
          "Categories"
        ],
        "summary": "Delete a category",
        "description": "Deletes an empty category and its image. A category that still holds products or subcategories is refused with 409 `category_not_empty`: move the products and re-parent or delete the subcategories first. The change is recorded in the change history and can be reverted with `POST /v1/changes/{id}/undo`. The undo re-creates the category under its original id, without its image, and fails if that id has been taken since. Every write requires an `Idempotency-Key` header; re-sending the same key with a different body returns 422 `idempotency_key_reuse`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The category was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "deleted",
                        "id"
                      ],
                      "properties": {
                        "deleted": {
                          "type": "boolean",
                          "const": true
                        },
                        "id": {
                          "type": "integer"
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/customers": {
      "get": {
        "operationId": "listCustomers",
        "tags": [
          "Customers"
        ],
        "summary": "List customers",
        "description": "Lists the store's customers, newest first. Cursor-paginated: data holds items, next_cursor and has_more. There are no date, search or sort filters.",
        "security": [
          {
            "dzOAuth": [
              "customers:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "phone",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Exact match on the stored phone."
          },
          {
            "name": "email",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Exact match on the stored email."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of customers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CustomerList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/customers/{id}": {
      "get": {
        "operationId": "getCustomer",
        "tags": [
          "Customers"
        ],
        "summary": "Get a customer",
        "description": "Returns one customer with address, merchant notes and fraud score.",
        "security": [
          {
            "dzOAuth": [
              "customers:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Numeric id. A value that is not all digits returns 400."
          }
        ],
        "responses": {
          "200": {
            "description": "The customer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Customer"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/customers/{id}/orders": {
      "get": {
        "operationId": "listCustomerOrders",
        "tags": [
          "Customers"
        ],
        "summary": "List a customer's orders",
        "description": "Lists the orders linked to one customer, newest first, with id, number, status, payment status, total and creation time. Cursor-paginated: data holds items, next_cursor and has_more. An unknown customer, or one from another store, returns 404.",
        "security": [
          {
            "dzOAuth": [
              "customers:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Numeric id. A value that is not all digits returns 400."
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the customer's orders.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CustomerOrderList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/landing-page-section-types": {
      "get": {
        "operationId": "listLandingPageSectionTypes",
        "tags": [
          "Landing pages"
        ],
        "summary": "List the section types you can create",
        "description": "Returns the 14 section types the API can create, each with the full set of settings keys it stores and their default values. Read this before writing sections so key names match what the page renders.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The section type catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "section_types"
                      ],
                      "properties": {
                        "section_types": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/LandingPageSectionType"
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/landing-pages": {
      "get": {
        "operationId": "listLandingPages",
        "tags": [
          "Landing pages"
        ],
        "summary": "List landing pages",
        "description": "Lists the store's landing pages, newest first. Cursor-paginated: data is {items, next_cursor, has_more}. An unrecognised status filter is ignored and every page is returned. On api.dzbuild.app this list may be served from a 30-second cache, marked by the X-Cache response header (HIT or MISS).",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by status. Other values are ignored.",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "draft"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of landing pages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "items",
                        "next_cursor",
                        "has_more"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/LandingPageSummary"
                          }
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "has_more": {
                          "type": "boolean"
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createLandingPage",
        "tags": [
          "Landing pages"
        ],
        "summary": "Create a landing page",
        "description": "Creates a landing page record with no sections, then returns it in the same shape as GET /v1/landing-pages/{id}. The response status is 200, not 201. Plan limits apply and count drafts: Free stores need a purchased page slot, Pro allows 3 pages, Unlimited and Enterprise have no cap; a store at its limit gets 403 with error code limit_reached. Validation failures (missing or over-long title, a product_id from another store) return 400 bad_request. Reusing an Idempotency-Key with a different body returns 422 idempotency_key_reuse. The response reads the landing page back, so the key needs `landing_pages:read` as well as `landing_pages:write`: with `landing_pages:write` alone the change is saved and the call answers 403 `forbidden`, and a retry with the same Idempotency-Key replays that 403.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:write",
              "landing_pages:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LandingPageInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new landing page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPage"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/landing-pages/generate/{id}": {
      "get": {
        "operationId": "getLandingPageGeneration",
        "tags": [
          "Landing pages"
        ],
        "summary": "Get the status of an AI landing page generation",
        "description": "Returns the progress of a generation started by POST /v1/landing-pages/generate. When status is completed, landing_page_id names the new page. A run still in progress after 10 minutes is marked failed with error timeout at the next poll and its credits are refunded. A non-numeric id returns 400; a task of another store returns 404.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Task id returned by POST /v1/landing-pages/generate.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The task status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPageGenerationStatus"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/landing-pages/{id}": {
      "get": {
        "operationId": "getLandingPage",
        "tags": [
          "Landing pages"
        ],
        "summary": "Get a landing page",
        "description": "Returns one landing page of the store with its SEO fields and a live section count. A non-numeric id returns 400; an id outside the store returns 404.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Landing page id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The landing page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPage"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateLandingPage",
        "tags": [
          "Landing pages"
        ],
        "summary": "Update a landing page",
        "description": "Changes the fields sent and returns the page. Validation is strict here: an invalid title, status or language, or a product_id from another store, returns 400 bad_request. Changing the slug, or renaming without sending a slug, keeps the old address working. Setting status to active runs the same check as GET /v1/landing-pages/{id}/check and is refused with 422 page_not_publishable when a blocking problem exists; the error carries a problems list. Editing a page that is already live is never blocked. Other 422 code: idempotency_key_reuse. The response reads the landing page back, so the key needs `landing_pages:read` as well as `landing_pages:write`: with `landing_pages:write` alone the change is saved and the call answers 403 `forbidden`, and a retry with the same Idempotency-Key replays that 403.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:write",
              "landing_pages:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Landing page id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LandingPageUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated landing page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPage"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteLandingPage",
        "tags": [
          "Landing pages"
        ],
        "summary": "Delete a landing page",
        "description": "Deletes the landing page and its sections. This cannot be undone. Returns 404 when the page is not the store's. 422 code: idempotency_key_reuse.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Landing page id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The page was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPageDeleted"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/landing-pages/{id}/check": {
      "get": {
        "operationId": "checkLandingPage",
        "tags": [
          "Landing pages"
        ],
        "summary": "Check a landing page before publishing",
        "description": "Reports what a buyer would find broken on the page. Blocking problems stop a publish: empty_page, order_form_without_product (an order-taking section with no product on the page or the section, so orders would land at 0 DA) and section_product_not_found. Warnings are advice: no_order_form, multiple_order_forms and variants_need_page_product (variant pickers only render from the page's own product).",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Landing page id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The check report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPageCheck"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/landing-pages/{id}/publish": {
      "post": {
        "operationId": "publishLandingPage",
        "tags": [
          "Landing pages"
        ],
        "summary": "Publish a landing page",
        "description": "Sets the page live, the same as PATCH with status active, and returns the page. Refused with 422 page_not_publishable while the page has a blocking problem (no sections, an order form with no product, or a section pointing at another store's product); the error carries the problems list from GET /v1/landing-pages/{id}/check. Other 422 code: idempotency_key_reuse. No request body. The response reads the landing page back, so the key needs `landing_pages:read` as well as `landing_pages:write`: with `landing_pages:write` alone the change is saved and the call answers 403 `forbidden`, and a retry with the same Idempotency-Key replays that 403.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:write",
              "landing_pages:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Landing page id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The published landing page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPage"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/landing-pages/{id}/sections": {
      "get": {
        "operationId": "listLandingPageSections",
        "tags": [
          "Landing pages"
        ],
        "summary": "List a landing page's sections",
        "description": "Returns every section of the page in render order with its full settings, up to 500 sections. Not paginated: data holds landing_page_id and the sections list.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Landing page id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The page's sections.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPageSectionList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "addLandingPageSection",
        "tags": [
          "Landing pages"
        ],
        "summary": "Add a section to a landing page",
        "description": "Appends one section at the end of the page. Settings are merged over the type's defaults, so a section can be created fully configured in one call. An order_form, order_button or product_offers section needs a product on the page or in settings.product_id; without one the call is refused. 422 codes: invalid_section_type, landing_page_has_no_product, validation_error (settings.product_id from another store), too_many_items (more than 20 slides or 30 items), settings_too_large, idempotency_key_reuse. 404 code: landing_page_not_found. A settings value that is not an object returns 400.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Landing page id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LandingPageSectionInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The section was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPageSectionCreated"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/landing-pages/{id}/sections/batch": {
      "post": {
        "operationId": "addLandingPageSections",
        "tags": [
          "Landing pages"
        ],
        "summary": "Add several sections to a landing page",
        "description": "Appends up to 30 sections in one call, all or nothing: every entry is validated before any is saved. The same rules as adding one section apply to each entry. 422 codes: validation_error (empty list, an entry or its settings not an object, settings.product_id from another store), too_many_items (more than 30 entries, or a list setting over its cap), invalid_section_type, landing_page_has_no_product, settings_too_large, idempotency_key_reuse. 404 code: landing_page_not_found. A body without a sections array returns 400.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Landing page id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LandingPageSectionBatchInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The sections were created, in the order sent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPageSectionBatchCreated"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/landing-pages/{id}/sections/reorder": {
      "post": {
        "operationId": "reorderLandingPageSections",
        "tags": [
          "Landing pages"
        ],
        "summary": "Reorder a landing page's sections",
        "description": "Sets the render order from a list that names every section of the page exactly once; positions become 0, 1, 2 in the order sent. Returns the sections in their new order. 422 code validation_error for a non-id entry, a repeated id or a list that leaves a section out; also idempotency_key_reuse. 404 codes: landing_page_not_found, section_not_found (an id that is not on this page). A body without an array returns 400.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Landing page id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LandingPageSectionReorderInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sections in their new order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPageSectionList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/landing-pages/{id}/sections/{section_id}": {
      "patch": {
        "operationId": "updateLandingPageSection",
        "tags": [
          "Landing pages"
        ],
        "summary": "Update a landing page section",
        "description": "Changes one section's settings. By default the sent settings are merged over what is stored; send replace true to start again from the type's defaults. The section type cannot change. 422 codes: validation_error (settings.product_id from another store), landing_page_has_no_product, too_many_items, settings_too_large, idempotency_key_reuse. 404 codes: landing_page_not_found, section_not_found. A missing or non-object settings value returns 400.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Landing page id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "section_id",
            "in": "path",
            "required": true,
            "description": "Section id on this landing page.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LandingPageSectionUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The section after the update.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPageSectionResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteLandingPageSection",
        "tags": [
          "Landing pages"
        ],
        "summary": "Delete a landing page section",
        "description": "Removes one section from the page. Other sections keep their positions. 404 codes: landing_page_not_found, section_not_found. 422 code: idempotency_key_reuse.",
        "security": [
          {
            "dzOAuth": [
              "landing_pages:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Landing page id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "section_id",
            "in": "path",
            "required": true,
            "description": "Section id on this landing page.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The section was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LandingPageSectionDeleted"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/orders": {
      "get": {
        "operationId": "listOrders",
        "tags": [
          "Orders"
        ],
        "summary": "List orders",
        "description": "Lists the store's orders, newest first, as compact rows without items. Cursor-paginated: data holds items, next_cursor and has_more. An unknown status value or an unparseable since is ignored and the list comes back unfiltered.",
        "security": [
          {
            "dzOAuth": [
              "orders:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/OrderStatus"
            },
            "description": "Only orders in this status. Any other value is ignored and the list is returned unfiltered."
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only orders created at or after this date or date-time (any common format, for example 2026-04-30T20:00:00Z)."
          },
          {
            "name": "customer_phone",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Exact match on the order's phone as stored."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of orders.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/OrderList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-dz-presets": [
          "cloudflare-orders"
        ]
      },
      "post": {
        "operationId": "createOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Create an order",
        "description": "Creates a pending order for the store and returns it in the same shape as GET /v1/orders/{id}, with HTTP 200. Line prices come from the catalogue, variant adjustments are checked against the product, and shipping is quoted from the store's own rates, free-shipping rules and weight surcharge, exactly as for a manual dashboard order; a price, shipping_cost or payment_fee sent in the body is ignored. Stock moves on the first confirmation, not at create. The merchant gets the usual new-order notification and the order.created webhook fires. A store on a plan with a monthly order cap gets 400 once the cap is reached. Error messages are bad_request with the failing field named, for example customer.wilaya_id must be 1-58. Requires an Idempotency-Key header; a retry with the same key and body within 24 hours replays the first answer with Idempotency-Replay: 1, and the same key with a different body returns 422 idempotency_key_reuse. 5xx and 429 answers are never replayed. The response reads the order back, so the key needs `orders:read` as well as `orders:write`: with `orders:write` alone the change is saved and the call answers 403 `forbidden`, and a retry with the same Idempotency-Key replays that 403.",
        "security": [
          {
            "dzOAuth": [
              "orders:write",
              "orders:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Order"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/orders/{id}": {
      "get": {
        "operationId": "getOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Get an order",
        "description": "Returns one order with its customer block, delivery choice, courier shipment state, amounts and line items with the chosen variants. Order notes, product names, SKUs and line totals are not returned.",
        "security": [
          {
            "dzOAuth": [
              "orders:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Numeric id. A value that is not all digits returns 400."
          }
        ],
        "responses": {
          "200": {
            "description": "The order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Order"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Change an order's status",
        "description": "Moves the order to a new status. Only the moves listed under OrderStatus are allowed; anything else returns 400 with the statuses reachable from the current one. Sending the status the order already has returns 200 and changes nothing. Entering confirmed, processing, shipped or delivered for the first time takes the stock; cancelled or returned from one of those puts it back. The merchant's usual status notifications run, and order.confirmed, order.shipped, order.delivered, order.cancelled or order.returned fires. A move that races another change returns 400 order status changed concurrently; retry. That 400 is stored against the Idempotency-Key like any 4xx, so retry with a new key. Requires an Idempotency-Key header; a retry with the same key and body within 24 hours replays the first answer with Idempotency-Replay: 1, and the same key with a different body returns 422 idempotency_key_reuse. 5xx and 429 answers are never replayed. The response reads the order back, so the key needs `orders:read` as well as `orders:write`: with `orders:write` alone the change is saved and the call answers 403 `forbidden`, and a retry with the same Idempotency-Key replays that 403.",
        "security": [
          {
            "dzOAuth": [
              "orders:write",
              "orders:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Numeric id. A value that is not all digits returns 400."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The order after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Order"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/orders/{id}/cancel": {
      "post": {
        "operationId": "cancelOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Cancel an order",
        "description": "Same as PATCH /v1/orders/{id} with status cancelled. Allowed from pending, confirmed and processing; a shipped or delivered order returns 400. Stock taken by an earlier confirmation is put back. No body is needed. Requires an Idempotency-Key header; a retry with the same key and body within 24 hours replays the first answer with Idempotency-Replay: 1, and the same key with a different body returns 422 idempotency_key_reuse. 5xx and 429 answers are never replayed. The response reads the order back, so the key needs `orders:read` as well as `orders:write`: with `orders:write` alone the change is saved and the call answers 403 `forbidden`, and a retry with the same Idempotency-Key replays that 403.",
        "security": [
          {
            "dzOAuth": [
              "orders:write",
              "orders:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Numeric id. A value that is not all digits returns 400."
          }
        ],
        "responses": {
          "200": {
            "description": "The cancelled order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Order"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/orders/{id}/send-to-delivery": {
      "post": {
        "operationId": "sendOrderToDelivery",
        "tags": [
          "Orders"
        ],
        "summary": "Hand an order to the courier",
        "description": "Creates the parcel with the store's courier, the same send the dashboard button performs. It cannot be undone, so the first call always answers 422 confirmation_required with a confirm_token (valid 600 seconds), the action name and a will_change summary (customer, phone, destination, delivery type, total, courier). Show that summary to the merchant and repeat the call with confirm_token and a new Idempotency-Key once they approve (the first key is bound to the body without the token); a token that expired, was used, or no longer matches the order answers 422 confirmation_stale with a fresh one. On success the send normally runs in the background and returns 202: read GET /v1/orders/{id} until shipment.sent_to_delivery is true, or check shipment.last_send_failure. When the background runner is unavailable the send happens in the request and returns 200 with the tracking number, or 422 courier_refused with the courier's message. An order already handed over returns 409 already_sent with error.tracking. On top of the store's request limit, a per-store courier budget of 6 calls per minute and 2 running at once is shared by POST /v1/orders/{id}/send-to-delivery, POST /v1/shipping/providers, POST /v1/shipping/providers/test and POST /v1/shipping/rates/sync. Error codes: confirmation_required, confirmation_stale, courier_refused, idempotency_key_reuse (422); already_sent (409); rate_limited and too_many_concurrent (429). Requires an Idempotency-Key header; a retry with the same key and body within 24 hours replays the first answer with Idempotency-Replay: 1, and the same key with a different body returns 422 idempotency_key_reuse. 5xx and 429 answers are never replayed. Merchant keys do not carry `delivery:send`, so a merchant key gets 403 `forbidden` here; assistant connections are granted it.",
        "security": [
          {
            "dzOAuth": [
              "delivery:send"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Numeric id. A value that is not all digits returns 400."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderSendToDeliveryInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent in the request (fallback path).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/OrderSendResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "202": {
            "description": "Send scheduled in the background.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/OrderSendQueued"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/orders/{id}/whatsapp": {
      "post": {
        "operationId": "sendOrderWhatsApp",
        "tags": [
          "WhatsApp"
        ],
        "summary": "Send a WhatsApp order message to the buyer",
        "description": "Queues one platform-approved template for the order's buyer and takes one message from the store's WhatsApp wallet. `template` is a key from GET /v1/whatsapp/templates, or `shipped`, which picks shipped_home, shipped_desk or desk_ready from the order's delivery type. `language` defaults to the language set in the addon. The addon's automatic-message switches do not apply to this call. The message is sent within about a minute; follow it with GET /v1/whatsapp/messages?order_id={id}. A send that WhatsApp rejects is marked failed and its credit returns to the wallet. Each template can be sent once per order through the API: a second call answers 409 already_sent with error.id and error.status, unless the earlier attempt was skipped (for example an invalid phone number that has since been corrected), in which case it is attempted again. A French message from a store whose delivery delay text is in Arabic carries that Arabic text. Error codes: unknown_template, invalid_language, invalid_number, suppressed, template_not_approved, empty_param, idempotency_key_reuse (422); addon_not_active (403); no_credit (402); already_sent (409); send_failed (500). Requires an Idempotency-Key header; a retry with the same key and body within 24 hours replays the first answer with Idempotency-Replay: 1, including a 402 or 422, so retry with a new key after topping up the wallet or fixing the order. The same key with a different body returns 422 idempotency_key_reuse. 5xx and 429 answers are never replayed.",
        "security": [
          {
            "dzOAuth": [
              "whatsapp:send"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Numeric order id. A value that is not all digits returns 400."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WhatsAppSendInput"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued and paid; the message leaves within about a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WhatsAppSendQueued"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "error.code is already_sent: this template was already sent for this order through the API. error.id and error.status name that message.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhatsAppAlreadySent"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/ping": {
      "get": {
        "operationId": "ping",
        "tags": [
          "Connection"
        ],
        "summary": "Check that the API answers",
        "description": "Needs no key. Answers `pong: true` with the current time; use it to check connectivity. Through `api.dzbuild.app` the edge answers directly and adds `edge: true`.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The API is reachable.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Ping"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/pixels": {
      "get": {
        "operationId": "listPixels",
        "tags": [
          "Pixels"
        ],
        "summary": "List tracking pixels",
        "description": "Returns the store's tracking pixels, newest first, as a cursor list (`items`, `next_cursor`, `has_more`) plus `limits`, the plan allowance and per-type usage. Access tokens are never returned.",
        "security": [
          {
            "dzOAuth": [
              "pixels:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "pixel_type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only pixels of this type (facebook, tiktok, snapchat, pinterest, google_analytics, gtm, google_ads). The value is matched as given; an unknown type returns an empty list."
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of pixels.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "items",
                        "next_cursor",
                        "has_more",
                        "limits"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Pixel"
                          }
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Pass as `cursor` to fetch the next page; null on the last page."
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "limits": {
                          "$ref": "#/components/schemas/PixelLimits"
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createPixel",
        "tags": [
          "Pixels"
        ],
        "summary": "Add a tracking pixel",
        "description": "Adds a pixel and returns it with 201. Checks are syntactic only: a 201 means the ids are well formed, not that the ad platform accepted them. The plan decides how many pixels a store may have. The change is recorded in the change history and can be reverted with `POST /v1/changes/{id}/undo`. Every write requires an `Idempotency-Key` header; re-sending the same key with a different body returns 422 `idempotency_key_reuse`. Conflict codes: `limit_reached` (plan allowance used up), `pixel_exists` (same type and pixel_id already on the store). Unprocessable codes: `invalid_pixel_type`, `invalid_pixel_id`, `invalid_access_token`, `pixel_write_failed`, `idempotency_key_reuse`.",
        "security": [
          {
            "dzOAuth": [
              "pixels:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PixelInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created pixel.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Pixel"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/pixels/{id}": {
      "patch": {
        "operationId": "updatePixel",
        "tags": [
          "Pixels"
        ],
        "summary": "Update a tracking pixel",
        "description": "Changes only the fields you send. pixel_type and pixel_id are fixed at creation: delete the pixel and add a new one to change them. An empty or masked access_token keeps the stored token. An empty body changes nothing and returns the pixel. The change is recorded in the change history and can be reverted with `POST /v1/changes/{id}/undo`. The undo restores every field except the access token. Every write requires an `Idempotency-Key` header; re-sending the same key with a different body returns 422 `idempotency_key_reuse`. Unprocessable codes: `immutable_field`, `invalid_access_token`, `pixel_write_failed`, `idempotency_key_reuse`.",
        "security": [
          {
            "dzOAuth": [
              "pixels:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PixelUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated pixel.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Pixel"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deletePixel",
        "tags": [
          "Pixels"
        ],
        "summary": "Delete a tracking pixel",
        "description": "Deletes the pixel and its product, category and landing page assignments. The change is recorded in the change history and can be reverted with `POST /v1/changes/{id}/undo`. The undo re-creates the pixel without its access token and without its assignments. Every write requires an `Idempotency-Key` header; re-sending the same key with a different body returns 422 `idempotency_key_reuse`.",
        "security": [
          {
            "dzOAuth": [
              "pixels:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The pixel was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "deleted",
                        "id"
                      ],
                      "properties": {
                        "deleted": {
                          "type": "boolean",
                          "const": true
                        },
                        "id": {
                          "type": "integer"
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/products": {
      "get": {
        "operationId": "listProducts",
        "tags": [
          "Products"
        ],
        "summary": "List products",
        "description": "Returns the store's products, newest first, as a cursor-paginated list (`items`, `next_cursor`, `has_more`). `status` filters by status; any other value is ignored and the list is unfiltered, which includes archived products. `search` matches part of the name or the exact SKU. On api.dzbuild.app this list is cached for 30 seconds per key; the `X-Cache` response header says HIT or MISS.",
        "security": [
          {
            "dzOAuth": [
              "products:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "draft",
                "archived"
              ]
            },
            "description": "Filter by status."
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Part of the product name, or an exact SKU."
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of products.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-dz-presets": [
          "cloudflare-catalog"
        ]
      },
      "post": {
        "operationId": "createProduct",
        "tags": [
          "Products"
        ],
        "summary": "Create a product",
        "description": "Creates a product and answers 200 with the same body as GET /v1/products/{id}. The slug is derived from the name. Validation failures, a category from another store and the plan limit on active products (Free 5, Pro 300, Unlimited and Enterprise no limit; drafts do not count) answer 400 `bad_request` with a message naming the problem. Requires an Idempotency-Key header (1 to 64 characters from A-Z, a-z, 0-9, `_-:.`); a missing or malformed key answers 400. Reusing a key with a different method, path or body answers 422 `idempotency_key_reuse`; a replayed response carries `Idempotency-Replay: 1`. The only 422 code is `idempotency_key_reuse`. The response reads the product back, so the key needs `products:read` as well as `products:write`: with `products:write` alone the change is saved and the call answers 403 `forbidden`, and a retry with the same Idempotency-Key replays that 403.",
        "security": [
          {
            "dzOAuth": [
              "products:write",
              "products:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created product.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Product"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/products/{id}": {
      "get": {
        "operationId": "getProduct",
        "tags": [
          "Products"
        ],
        "summary": "Get a product with images and variants",
        "description": "Returns one product with pricing, inventory, shipping fields, images (primary first) and variants. Combinations are listed up to 300; `combination_count` gives the true total.",
        "security": [
          {
            "dzOAuth": [
              "products:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The product.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Product"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateProduct",
        "tags": [
          "Products"
        ],
        "summary": "Update a product",
        "description": "Changes only the fields sent and answers with the full product. Renaming regenerates the slug unless `slug` is sent too. Invalid values answer 400 `bad_request`. Unlike create, this call does not check the plan limit on active products. Requires an Idempotency-Key header (1 to 64 characters from A-Z, a-z, 0-9, `_-:.`); a missing or malformed key answers 400. Reusing a key with a different method, path or body answers 422 `idempotency_key_reuse`; a replayed response carries `Idempotency-Replay: 1`. The only 422 code is `idempotency_key_reuse`. The response reads the product back, so the key needs `products:read` as well as `products:write`: with `products:write` alone the change is saved and the call answers 403 `forbidden`, and a retry with the same Idempotency-Key replays that 403.",
        "security": [
          {
            "dzOAuth": [
              "products:write",
              "products:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated product.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Product"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteProduct",
        "tags": [
          "Products"
        ],
        "summary": "Delete a product",
        "description": "Permanently deletes the product with its images, variants, offers and input fields. This cannot be undone; setting status to archived keeps the product and its history. Requires an Idempotency-Key header (1 to 64 characters from A-Z, a-z, 0-9, `_-:.`); a missing or malformed key answers 400. Reusing a key with a different method, path or body answers 422 `idempotency_key_reuse`; a replayed response carries `Idempotency-Replay: 1`. The only 422 code is `idempotency_key_reuse`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The product was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductDeleted"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/products/{id}/addons": {
      "get": {
        "operationId": "getProductAddons",
        "tags": [
          "Products"
        ],
        "summary": "Get a product's custom input fields",
        "description": "Returns the extra fields buyers fill in on the product page (text, long text or image upload) and the master switch that shows or hides them.",
        "security": [
          {
            "dzOAuth": [
              "products:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The input fields.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductAddons"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "setProductAddons",
        "tags": [
          "Products"
        ],
        "summary": "Replace a product's custom input fields",
        "description": "Replaces every custom input field of the product with the list sent (at most 20; [] removes them all) and optionally sets the master switch. Answers with the new state. 422 codes: `validation_error`, `idempotency_key_reuse`. Requires an Idempotency-Key header (1 to 64 characters from A-Z, a-z, 0-9, `_-:.`); a missing or malformed key answers 400. Reusing a key with a different method, path or body answers 422 `idempotency_key_reuse`; a replayed response carries `Idempotency-Replay: 1`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductAddonsInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new input fields.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductAddons"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/products/{id}/images": {
      "post": {
        "operationId": "addProductImage",
        "tags": [
          "Products"
        ],
        "summary": "Add an image from a public URL",
        "description": "Downloads the image at a public https URL, re-encodes it and attaches it to the product. Answers 201 with the new image, or 200 with `deduplicated: true` when the same bytes are already attached. The first image of a product becomes the primary one. Limits: 20 images per product, 20 MB per file, 10000 by 10000 pixels; JPEG, PNG, WebP, GIF, BMP, AVIF, HEIC/HEIF and TIFF are accepted. The URL must use a hostname (not an IP address) that resolves to public addresses, port 443, no credentials, and must answer directly without a redirect. This endpoint has its own per-store budget on top of the general rate limit: 10 calls per minute and 3 running at once; going over answers 429 `rate_limited` or 429 `too_many_concurrent`. 422 codes: `validation_error`, `too_many_images`, `url_refused`, `image_fetch_failed`, `unsupported_image`, `image_too_large`, `idempotency_key_reuse`. Requires an Idempotency-Key header (1 to 64 characters from A-Z, a-z, 0-9, `_-:.`); a missing or malformed key answers 400. Reusing a key with a different method, path or body answers 422 `idempotency_key_reuse`; a replayed response carries `Idempotency-Replay: 1`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductImageInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The image was already attached; the existing image is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductImageAddResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "201": {
            "description": "The image was added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductImageAddResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/products/{id}/images/{image_id}": {
      "patch": {
        "operationId": "updateProductImage",
        "tags": [
          "Products"
        ],
        "summary": "Update an image's alt text, order or primary flag",
        "description": "Updates alt text and display order, or makes the image the primary one. A product always keeps exactly one primary image, so `is_primary: false` is refused. 422 codes: `validation_error` (sort_order outside 0 to 999), `primary_required`, `idempotency_key_reuse`. Requires an Idempotency-Key header (1 to 64 characters from A-Z, a-z, 0-9, `_-:.`); a missing or malformed key answers 400. Reusing a key with a different method, path or body answers 422 `idempotency_key_reuse`; a replayed response carries `Idempotency-Replay: 1`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "name": "image_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Image id. Must be digits only and belong to this product."
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductImageUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated image.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductImageUpdateResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteProductImage",
        "tags": [
          "Products"
        ],
        "summary": "Delete an image",
        "description": "Deletes the image. Variant options that pointed at it keep existing but lose the picture, and deleting the primary image promotes the next one. Landing-page image sections that showed the file switch to the product's next image. Requires an Idempotency-Key header (1 to 64 characters from A-Z, a-z, 0-9, `_-:.`); a missing or malformed key answers 400. Reusing a key with a different method, path or body answers 422 `idempotency_key_reuse`; a replayed response carries `Idempotency-Replay: 1`. The only 422 code is `idempotency_key_reuse`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "name": "image_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Image id. Must be digits only and belong to this product."
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The image was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductImageDeleted"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/products/{id}/offers": {
      "get": {
        "operationId": "getProductOffers",
        "tags": [
          "Products"
        ],
        "summary": "Get a product's quantity offers",
        "description": "Returns the quantity bundles shown on the product page. `price` is the total for the whole bundle, not a unit price; an offer with `discount_type` has price 0 and takes `discount_value` off the product price times the quantity instead.",
        "security": [
          {
            "dzOAuth": [
              "products:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The offers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductOffers"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "setProductOffers",
        "tags": [
          "Products"
        ],
        "summary": "Replace a product's quantity offers",
        "description": "Replaces every quantity offer of the product with the list sent (at most 50; [] removes them all) and answers with the new state. `price` is the total for the bundle. Offer pictures cannot be uploaded here: send back the `image_path` from GET to keep one, and pictures no longer used are deleted. 422 codes: `validation_error`, `duplicate_offer_quantity`, `invalid_image_path`, `idempotency_key_reuse`. Requires an Idempotency-Key header (1 to 64 characters from A-Z, a-z, 0-9, `_-:.`); a missing or malformed key answers 400. Reusing a key with a different method, path or body answers 422 `idempotency_key_reuse`; a replayed response carries `Idempotency-Replay: 1`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductOffersInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new offers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductOffers"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/products/{id}/quantity-rules": {
      "get": {
        "operationId": "getProductQuantityRules",
        "tags": [
          "Products"
        ],
        "summary": "Get a product's minimum and maximum order quantity",
        "description": "Returns the per-product minimum and maximum order quantity (0 means no limit) and whether the Minimum & Maximum Quantity Per Product add-on is active. A rule is only enforced at checkout while that add-on is active; `warning` says so when it is not.",
        "security": [
          {
            "dzOAuth": [
              "products:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The quantity rule.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductQuantityRules"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "setProductQuantityRules",
        "tags": [
          "Products"
        ],
        "summary": "Set a product's minimum and maximum order quantity",
        "description": "Sets the minimum and maximum order quantity for the product. 0 means no limit, and sending 0 for both removes the rule. A maximum below the minimum is refused because it would block every order. 422 codes: `validation_error`, `idempotency_key_reuse`. Requires an Idempotency-Key header (1 to 64 characters from A-Z, a-z, 0-9, `_-:.`); a missing or malformed key answers 400. Reusing a key with a different method, path or body answers 422 `idempotency_key_reuse`; a replayed response carries `Idempotency-Replay: 1`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductQuantityRulesInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved rule.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductQuantityRules"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/products/{id}/stock": {
      "get": {
        "operationId": "getProductStock",
        "tags": [
          "Products"
        ],
        "summary": "Get a product's stock",
        "description": "Returns the product's stock mode (product level, per variant option, or per combination) and the current count of every target that can be set in that mode.",
        "security": [
          {
            "dzOAuth": [
              "products:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The stock picture.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductStock"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "setProductStock",
        "tags": [
          "Products"
        ],
        "summary": "Adjust a product's stock",
        "description": "Sets or shifts stock counts. Each item names a target that matches the product's stock mode; a target from another mode answers 409 `stock_mode_mismatch`. An unknown option or combination id answers 404. The change is recorded in the store's change history and can be undone, and it answers with the stock picture after the change. This is a manual adjustment: it does not touch order stock bookkeeping. 422 codes: `validation_error`, `option_stock_unlimited`, `idempotency_key_reuse`. Requires an Idempotency-Key header (1 to 64 characters from A-Z, a-z, 0-9, `_-:.`); a missing or malformed key answers 400. Reusing a key with a different method, path or body answers 422 `idempotency_key_reuse`; a replayed response carries `Idempotency-Replay: 1`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductStockInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stock picture after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductStock"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/products/{id}/variants": {
      "put": {
        "operationId": "replaceProductVariants",
        "tags": [
          "Products"
        ],
        "summary": "Replace all variants of a product",
        "description": "Replaces every variant group, option and combination of the product with the payload; anything left out is deleted, and `{\"groups\": []}` removes all variants. The payload is validated before anything is deleted, so a refused call leaves the variants as they were. The stock mode follows the payload: combinations sent means per-combination stock, option stock without combinations means per-option stock, neither leaves product-level stock in charge. Limits: 10 groups, 100 options per group, 200 options in total, 1000 combinations. Answers with the new variants block. An Idempotency-Key is optional on PUT; when sent, the answer is replayed for 24 hours with Idempotency-Replay: 1, and without it every call runs. 422 codes: `validation_error`, `invalid_image_id`, `combinations_not_applicable`, `duplicate_combination`.",
        "security": [
          {
            "dzOAuth": [
              "products:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Product id. Must be digits only, otherwise the call answers 400."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional on this PUT. When sent, a retry with the same key and the same body replays the stored answer for 24 hours; the same key with a different body is refused with 422 idempotency_key_reuse. Without it the call is never replayed.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64,
              "pattern": "^[A-Za-z0-9_:.-]+$"
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductVariantsInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new variants.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductVariants"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/promo-codes": {
      "get": {
        "operationId": "listPromoCodes",
        "tags": [
          "Promo codes"
        ],
        "summary": "List promo codes",
        "description": "Returns the store's promo codes, newest first, as a cursor list (`items`, `next_cursor`, `has_more`) plus `addon_enabled`, which tells you whether the Promo codes add-on is active. Listing works even when the add-on is off.",
        "security": [
          {
            "dzOAuth": [
              "promos:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "is_active",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "examples": [
                "true",
                "false",
                "1",
                "0"
              ]
            },
            "description": "`true`, `1`, `yes` or `on` returns active codes; any other non-empty value returns inactive codes. Omit for all."
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of promo codes.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "items",
                        "next_cursor",
                        "has_more",
                        "addon_enabled"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/PromoCode"
                          }
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Pass as `cursor` to fetch the next page; null on the last page."
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "addon_enabled": {
                          "type": "boolean",
                          "description": "Whether the Promo codes add-on is active. Writes fail with 409 while it is off."
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "createPromoCode",
        "tags": [
          "Promo codes"
        ],
        "summary": "Create a promo code",
        "description": "Creates a promo code and returns it with 201. The discount applies to the whole order subtotal; there are no per-product or per-customer limits. Writes need the Promo codes add-on active on the store; otherwise they return 409 `addon_inactive`. Setting a 100% percentage discount returns 422 `confirmation_required` with `confirm_token`, `confirm_token_expires_in` (600), `action` and a `will_change` summary in the error body; show the summary to the merchant and re-send the same request with `confirm_token` once they approve. An expired, used or mismatched token returns `confirmation_stale` with a fresh token. The change is recorded in the change history and can be reverted with `POST /v1/changes/{id}/undo`. Every write requires an `Idempotency-Key` header; re-sending the same key with a different body returns 422 `idempotency_key_reuse`. Conflict codes: `addon_inactive`, `code_exists`. Unprocessable codes: `invalid_code`, `invalid_discount_type`, `invalid_discount_value`, `invalid_min_order_amount`, `invalid_max_uses`, `invalid_starts_at`, `invalid_expires_at`, `expires_at_in_past`, `invalid_date_window`, `confirmation_required`, `confirmation_stale`, `idempotency_key_reuse`.",
        "security": [
          {
            "dzOAuth": [
              "promos:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PromoCodeInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created promo code.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PromoCode"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/promo-codes/{id}": {
      "patch": {
        "operationId": "updatePromoCode",
        "tags": [
          "Promo codes"
        ],
        "summary": "Update a promo code",
        "description": "Changes only the fields you send; `code` and `is_active` can be changed too. A new expires_at must be in the future, but an already expired code stays editable when you leave its dates alone. An empty body changes nothing and returns the code. Writes need the Promo codes add-on active on the store; otherwise they return 409 `addon_inactive`. Setting a 100% percentage discount returns 422 `confirmation_required` with `confirm_token`, `confirm_token_expires_in` (600), `action` and a `will_change` summary in the error body; show the summary to the merchant and re-send the same request with `confirm_token` once they approve. An expired, used or mismatched token returns `confirmation_stale` with a fresh token. The change is recorded in the change history and can be reverted with `POST /v1/changes/{id}/undo`. Every write requires an `Idempotency-Key` header; re-sending the same key with a different body returns 422 `idempotency_key_reuse`. Conflict codes: `addon_inactive`, `code_exists`. Unprocessable codes: the same as create.",
        "security": [
          {
            "dzOAuth": [
              "promos:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PromoCodeUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated promo code.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PromoCode"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deletePromoCode",
        "tags": [
          "Promo codes"
        ],
        "summary": "Delete a promo code",
        "description": "Deletes the promo code. Orders already placed with it keep their discount. Writes need the Promo codes add-on active on the store; otherwise they return 409 `addon_inactive`. The change is recorded in the change history and can be reverted with `POST /v1/changes/{id}/undo`. The undo re-creates the code with its recorded usage count, reusing the id when it is still free. Every write requires an `Idempotency-Key` header; re-sending the same key with a different body returns 422 `idempotency_key_reuse`.",
        "security": [
          {
            "dzOAuth": [
              "promos:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The promo code was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "deleted",
                        "id"
                      ],
                      "properties": {
                        "deleted": {
                          "type": "boolean",
                          "const": true
                        },
                        "id": {
                          "type": "integer"
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/shipping/coverage": {
      "get": {
        "operationId": "getShippingCoverage",
        "tags": [
          "Shipping"
        ],
        "summary": "Show which wilayas, communes and desks a linked courier serves",
        "description": "Reports what the store's courier knows about its own network. Without `wilaya_id` it returns a per-wilaya count of communes (home and desk) and stop desks. With `wilaya_id` it also returns the commune list, the desk list and `desk_send_allowed`, the same desk check send-to-delivery applies. Without `provider` it uses the courier send-to-delivery picks by default, otherwise the first linked one. `knowledge_synced_at` is the last time the courier's network data was refreshed; null when it never was. Error codes: 400 `bad_request` (wilaya_id outside 0 to 69), 422 `no_courier_linked`, 404 `provider_not_linked` (the named courier is not linked to this store).",
        "security": [
          {
            "dzOAuth": [
              "shipping:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "required": false,
            "description": "Courier slug from GET /v1/shipping/providers. Omit to use the default courier.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wilaya_id",
            "in": "query",
            "required": false,
            "description": "Restrict to one wilaya and add its communes, desks and desk verdict. 0 or omitted means every wilaya. A non-numeric value is read as 0.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 69,
              "default": 0
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Coverage for the chosen courier.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShippingCoverage"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/shipping/providers": {
      "get": {
        "operationId": "listShippingProviders",
        "tags": [
          "Shipping"
        ],
        "summary": "List every supported courier with this store's link state",
        "description": "Returns every courier the platform supports, whether or not the store has linked it, with the credential labels each one expects (`api_id` and `api_token` mean different things per courier), the optional extra fields, and the store's link state. A courier configured directly on the store settings also shows as linked, with `source` set to `store_row`. `is_send_default` marks the courier send-to-delivery uses when none is named. Credential values are never returned, only `has_id` and `has_token`. Full list, not paginated.",
        "security": [
          {
            "dzOAuth": [
              "shipping:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Courier registry joined with this store's links.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShippingProviderList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "linkShippingProvider",
        "tags": [
          "Shipping"
        ],
        "summary": "Link a courier or re-save its credentials",
        "description": "Tests the credentials against the courier live and saves them only when the courier accepts them. An empty `api_id` or `api_token` keeps the value already stored for this courier, so a linked courier can be re-saved without resending its secrets. The first courier a store links becomes the default; `set_default: true` makes any link the default. Linking is not recorded in the change log (credentials are never stored there), so it cannot be undone through POST /v1/changes/{id}/undo; the response says `undoable: false`. On top of the store's request limit, a per-store courier budget of 6 calls per minute and 2 running at once is shared by POST /v1/orders/{id}/send-to-delivery, POST /v1/shipping/providers, POST /v1/shipping/providers/test and POST /v1/shipping/rates/sync. Going over the per-minute part returns 429 `rate_limited` with a Retry-After header; going over the parallel part returns 429 `too_many_concurrent` (retry after about 5 seconds). Requires an Idempotency-Key header. A replayed response carries `Idempotency-Replay: 1`; reusing a key with a different body returns 422 `idempotency_key_reuse`. Error codes: 400 `provider_required`, `credentials_required`, `invalid_credentials_format`, `api_url_required`, `custom_name_required`, `invalid_delivery_tier`, `bad_request` (body is not a JSON object); 404 `store_not_found`; 422 `unsupported_provider`, `credentials_rejected`, `invalid_api_url`, `api_url_mismatch`, `invalid_delivery_tier` (economic on a courier other than guepex), `shipping_not_available` (digital-product store).",
        "security": [
          {
            "dzOAuth": [
              "shipping:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShippingProviderLinkInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The courier is linked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShippingProviderLinkResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/shipping/providers/default": {
      "post": {
        "operationId": "setDefaultShippingProvider",
        "tags": [
          "Shipping"
        ],
        "summary": "Make a linked courier the store default",
        "description": "Moves the store default to a courier already added from the provider list; new send-to-delivery pushes go there. A courier that is only configured on the store settings, not added from the provider list, returns 404. When the chosen courier is disabled the `warning` says send-to-delivery stays off until it is enabled again. Not recorded in the change log, so it cannot be undone through the changes endpoints. Requires an Idempotency-Key header. A replayed response carries `Idempotency-Replay: 1`; reusing a key with a different body returns 422 `idempotency_key_reuse`. Error codes: 400 `provider_required`, `bad_request`; 404 `provider_not_linked`, `store_not_found`; 422 `shipping_not_available`.",
        "security": [
          {
            "dzOAuth": [
              "shipping:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShippingProviderDefaultInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The default courier changed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShippingProviderDefaultResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/shipping/providers/test": {
      "post": {
        "operationId": "testShippingProvider",
        "tags": [
          "Shipping"
        ],
        "summary": "Test courier credentials without saving them",
        "description": "Calls the courier with the given credentials and reports whether it accepted them. Nothing is saved (`saved` is always false). An empty `api_id` or `api_token` falls back to the value stored for this courier, so a linked courier can be re-tested without holding its secrets. A rejected credential is a 200 with `ok: false` and the courier's own `message`, not an error. On top of the store's request limit, a per-store courier budget of 6 calls per minute and 2 running at once is shared by POST /v1/orders/{id}/send-to-delivery, POST /v1/shipping/providers, POST /v1/shipping/providers/test and POST /v1/shipping/rates/sync. Going over the per-minute part returns 429 `rate_limited` with a Retry-After header; going over the parallel part returns 429 `too_many_concurrent` (retry after about 5 seconds). Requires an Idempotency-Key header. A replayed response carries `Idempotency-Replay: 1`; reusing a key with a different body returns 422 `idempotency_key_reuse`. Error codes: 400 `provider_required`, `credentials_required`, `invalid_credentials_format`, `api_url_required`, `invalid_delivery_tier`, `bad_request`; 404 `store_not_found`; 422 `unsupported_provider`, `invalid_api_url`, `api_url_mismatch`, `invalid_delivery_tier`, `shipping_not_available`.",
        "security": [
          {
            "dzOAuth": [
              "shipping:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShippingProviderTestInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The courier's answer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShippingProviderTestResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/shipping/providers/{provider}": {
      "delete": {
        "operationId": "unlinkShippingProvider",
        "tags": [
          "Shipping"
        ],
        "summary": "Unlink a courier and delete its stored credentials",
        "description": "Deletes the courier's stored credentials permanently; this is not undoable. When the courier was the default, the oldest enabled courier becomes the default. When no courier is left, send-to-delivery stops for the whole store until a courier is linked again. The call needs a confirmation: the first request answers 422 `confirmation_required` with a `confirm_token` and a summary of the consequence; send the token back in the body after the merchant approves. Keys used by the in-dashboard assistant must use the token; other keys may send `confirm: true` instead. A token is single use, bound to this courier and to the store's current courier count, and expires after 10 minutes; a used, expired or outdated token returns 422 `confirmation_stale` with a fresh one. The confirmation error body adds `confirm_token`, `confirm_token_expires_in` (600 seconds), `action` and `will_change` (the summary to show the merchant) to the `error` object. Re-send with that `confirm_token` and a new Idempotency-Key: the first 422 is stored against the old key, so the same key with a different body is refused as `idempotency_key_reuse`. Requires an Idempotency-Key header. A replayed response carries `Idempotency-Replay: 1`; reusing a key with a different body returns 422 `idempotency_key_reuse`. Error codes: 400 `bad_request` (invalid slug or body); 404 `provider_not_linked` (only couriers added from the provider list can be unlinked here), `store_not_found`; 422 `confirmation_required`, `confirmation_stale`, `shipping_not_available`.",
        "security": [
          {
            "dzOAuth": [
              "shipping:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "description": "Courier slug, 1 to 50 characters of letters, digits, `_` or `-`. Matched case-insensitively.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,50}$"
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShippingConfirmInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The courier is unlinked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShippingProviderUnlinkResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/shipping/rates": {
      "get": {
        "operationId": "getShippingRates",
        "tags": [
          "Shipping"
        ],
        "summary": "Get the store's delivery prices per wilaya",
        "description": "Returns the home and desk delivery price, their on/off switches and the delivery time for every wilaya the store has a rate for, keyed by wilaya id. Wilayas with no rate row are absent. Also returns the store's wilaya mode and the limits the write endpoint enforces. Prices are in DZD.",
        "security": [
          {
            "dzOAuth": [
              "shipping:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The rate table.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShippingRateTable"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "operationId": "setShippingRates",
        "tags": [
          "Shipping"
        ],
        "summary": "Create or update delivery prices for some wilayas",
        "description": "Partial update keyed by wilaya id: only the wilayas in `rates` change. A field left out (or null) keeps its stored value; for a wilaya with no rate yet the defaults are price 0, both delivery types enabled and 3 days. Every price is rounded to two decimals and must be between 0 and 100000 DZD; `days` must be a whole number from 0 to 60. The prior values are recorded in the change log, so the write can be reverted with POST /v1/changes/{id}/undo (entity `shipping.rates`). Requires an Idempotency-Key header. A replayed response carries `Idempotency-Replay: 1`; reusing a key with a different body returns 422 `idempotency_key_reuse`. Error codes: 400 `bad_request` (`rates` missing or not an object), `invalid_rates`, `invalid_wilaya` (non-numeric key), `invalid_price` (not a number), `invalid_days` (not a whole number); 404 `store_not_found`; 422 `invalid_wilaya` (unknown wilaya id), `invalid_price` (negative or above the ceiling), `invalid_days` (out of range), `snapshot_too_large`, `shipping_not_available`.",
        "security": [
          {
            "dzOAuth": [
              "shipping:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShippingRatesInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The rates were saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShippingRatesSaved"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/shipping/rates/sync": {
      "post": {
        "operationId": "syncShippingRates",
        "tags": [
          "Shipping"
        ],
        "summary": "Replace delivery prices with the courier's own price list",
        "description": "Starts a background job that overwrites the store's prices for every wilaya the courier serves with the courier's prices. Poll GET /v1/shipping/rates for the result. Before anything is queued, the whole current rate table is saved to the change log; `change_id` undoes the sync through POST /v1/changes/{id}/undo. The call needs a confirmation: the first request answers 422 `confirmation_required` with a `confirm_token` and a summary; send the token back after the merchant approves. Keys used by the in-dashboard assistant must use the token; other keys may send `confirm: true` instead. When a sync for this courier is already running the call returns 202 with `status: already_running`, that sync's `sync_id` and a null `change_id`, without asking for confirmation. After a successful sync the same courier cannot be synced again for 5 minutes (429 `sync_cooldown`). The confirmation error body adds `confirm_token`, `confirm_token_expires_in` (600 seconds), `action` and `will_change` (the summary to show the merchant) to the `error` object. Re-send with that `confirm_token` and a new Idempotency-Key: the first 422 is stored against the old key, so the same key with a different body is refused as `idempotency_key_reuse`. On top of the store's request limit, a per-store courier budget of 6 calls per minute and 2 running at once is shared by POST /v1/orders/{id}/send-to-delivery, POST /v1/shipping/providers, POST /v1/shipping/providers/test and POST /v1/shipping/rates/sync. Going over the per-minute part returns 429 `rate_limited` with a Retry-After header; going over the parallel part returns 429 `too_many_concurrent` (retry after about 5 seconds). Requires an Idempotency-Key header. A replayed response carries `Idempotency-Replay: 1`; reusing a key with a different body returns 422 `idempotency_key_reuse`. Error codes: 400 `provider_required`, `bad_request`; 404 `store_not_found`; 422 `rate_sync_unsupported` (mdm and neardelivery have no price list), `provider_not_linked` (not linked or disabled), `store_wilaya_required` (Yalidine-family couriers need the store wilaya set), `confirmation_required`, `confirmation_stale`, `snapshot_too_large`, `snapshot_failed`, `shipping_not_available`; 429 `sync_cooldown`; 503 `sync_queue_failed`.",
        "security": [
          {
            "dzOAuth": [
              "shipping:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShippingRatesSyncInput"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The sync is queued or already running.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShippingRatesSyncResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/shipping/settings": {
      "get": {
        "operationId": "getShippingSettings",
        "tags": [
          "Shipping"
        ],
        "summary": "Get the free-shipping and wilaya-mode settings",
        "description": "Returns whether free shipping is on, the order amount above which shipping is free, whether that threshold is in effect, and the store's wilaya mode. A threshold of 0 or null means no threshold (`free_shipping_threshold_active` is false). The `notes` object explains those two rules in plain text.",
        "security": [
          {
            "dzOAuth": [
              "shipping:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The shipping settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShippingSettings"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateShippingSettings",
        "tags": [
          "Shipping"
        ],
        "summary": "Update the free-shipping and wilaya-mode settings",
        "description": "Changes any of `free_shipping`, `free_shipping_threshold` and `wilaya_mode`; send at least one. Other fields are ignored. Only wilaya mode \"58\" can be set here; switching a store to 69 wilayas is done from the dashboard. The prior values are recorded in the change log (entity `shipping.settings`), so the change can be undone. The response is the updated settings, without the `notes` object. Requires an Idempotency-Key header. A replayed response carries `Idempotency-Replay: 1`; reusing a key with a different body returns 422 `idempotency_key_reuse`. Error codes: 400 `nothing_to_update`, `invalid_free_shipping`, `invalid_threshold` (not a number), `invalid_wilaya_mode`, `bad_request`; 404 `store_not_found`; 422 `invalid_threshold` (negative or too large), `wilaya_mode_69_unsupported`, `shipping_not_available`.",
        "security": [
          {
            "dzOAuth": [
              "shipping:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShippingSettingsUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ShippingSettings"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/store": {
      "get": {
        "operationId": "getStore",
        "tags": [
          "Store"
        ],
        "summary": "Get the store profile",
        "description": "Returns the profile of the store the calling key belongs to: name, slug, language, images, the four brand colours, subdomain, custom domain and the public URL customers land on. Through `api.dzbuild.app` a successful answer is cached for 30 seconds per key and query string (`X-Cache: HIT` or `MISS`), so a change can take up to 30 seconds to show. Needs a key on an active Enterprise plan (first-party assistant keys excepted) that is enrolled while the API is in pilot mode; otherwise the call returns 403 `forbidden`.",
        "security": [
          {
            "dzOAuth": [
              "store:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The store profile.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Store"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateStore",
        "tags": [
          "Store"
        ],
        "summary": "Update the store settings profile",
        "description": "Partial update of the settings-page profile: name, description, location, contact details and search-engine verification tokens. Only the keys present in the body change; a key sent as null or empty clears it where the field allows (store_name cannot be empty). At least one accepted key is required. The change is recorded in the change log and can be undone. Requires an `Idempotency-Key` header (at most 64 characters of letters, digits, `_`, `-`, `:` and `.`); a missing or malformed key returns 400 `bad_request`, and reusing a key for a different body returns 422 `idempotency_key_reuse`. A repeat with the same key and body replays the stored answer for 24 hours with `Idempotency-Replay: 1`. Error codes on 422: `no_writable_fields`, `invalid_value`, `invalid_wilaya`, `invalid_email`, `idempotency_key_reuse`. 404 `store_not_found` if the store was deleted.",
        "security": [
          {
            "dzOAuth": [
              "store:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The fields written and their stored values.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreUpdateResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/store/design": {
      "get": {
        "operationId": "getStoreDesign",
        "tags": [
          "Store"
        ],
        "summary": "Get the storefront design with current values",
        "description": "Returns every customize field of the theme the store currently uses, grouped by section, with its current value, whether the API can write it and whether the store plan locks it. Through `api.dzbuild.app` a successful answer is cached for 30 seconds per key and query string (`X-Cache: HIT` or `MISS`), so a change can take up to 30 seconds to show. Needs a key on an active Enterprise plan (first-party assistant keys excepted) that is enrolled while the API is in pilot mode; otherwise the call returns 403 `forbidden`.",
        "security": [
          {
            "dzOAuth": [
              "store:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The design manifest with a `value` on every field.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreDesign"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateStoreDesign",
        "tags": [
          "Store"
        ],
        "summary": "Update storefront design fields",
        "description": "Partial update of the storefront design (colours, navbar, hero, product cards, checkout wording, SEO, social links and more). Only the keys present in the body change. Fields the plan does not allow are left untouched and reported in `skipped`; the call still succeeds when at least one field was written. `GET /v1/store/design/fields` lists the fields the current theme uses. The change is recorded in the change log and can be undone. Requires an `Idempotency-Key` header (at most 64 characters of letters, digits, `_`, `-`, `:` and `.`); a missing or malformed key returns 400 `bad_request`, and reusing a key for a different body returns 422 `idempotency_key_reuse`. A repeat with the same key and body replays the stored answer for 24 hours with `Idempotency-Replay: 1`. Error codes on 422: `no_writable_fields` (nothing writable in the body, including when every key was skipped), `invalid_value` (a non-scalar value or a value the store rejects), `invalid_url`, `idempotency_key_reuse`. 404 `store_not_found` if the store was deleted.",
        "security": [
          {
            "dzOAuth": [
              "store:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreDesignUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Written, skipped and stored values.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreDesignUpdateResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/store/design/fields": {
      "get": {
        "operationId": "listStoreDesignFields",
        "tags": [
          "Store"
        ],
        "summary": "List the design fields the current theme accepts",
        "description": "Returns the customize manifest for the store theme, grouped by section, with each field key, type, plan requirement, whether the API can write it, whether this store plan locks it and, for fixed-choice fields, the allowed values. Same shape as `GET /v1/store/design` without the `value` key. Through `api.dzbuild.app` a successful answer is cached for 30 seconds per key and query string (`X-Cache: HIT` or `MISS`), so a change can take up to 30 seconds to show. Needs a key on an active Enterprise plan (first-party assistant keys excepted) that is enrolled while the API is in pilot mode; otherwise the call returns 403 `forbidden`.",
        "security": [
          {
            "dzOAuth": [
              "store:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The design manifest without values.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreDesign"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/store/fast-checkout-theme": {
      "post": {
        "operationId": "setStoreFastCheckoutTheme",
        "tags": [
          "Store"
        ],
        "summary": "Switch the fast-checkout form theme",
        "description": "Switches the look of the fast-checkout order form on product pages. Changes no design field. The change is recorded in the change log and can be undone. Requires an `Idempotency-Key` header (at most 64 characters of letters, digits, `_`, `-`, `:` and `.`); a missing or malformed key returns 400 `bad_request`, and reusing a key for a different body returns 422 `idempotency_key_reuse`. A repeat with the same key and body replays the stored answer for 24 hours with `Idempotency-Replay: 1`. Errors: 422 `invalid_theme`, 404 `theme_not_found`, 403 `plan_required`, 503 `column_missing` while the feature is being rolled out, 404 `store_not_found`.",
        "security": [
          {
            "dzOAuth": [
              "store:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreThemeSelect"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The fast-checkout theme now in use.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreFastCheckoutThemeResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/store/home-layout": {
      "get": {
        "operationId": "getStoreHomeLayout",
        "tags": [
          "Store"
        ],
        "summary": "Get the home page sections",
        "description": "Returns the store theme, whether that theme shows stored sections (rendered), the section cap for the plan, the sections in render order and the section types that can be added on this theme with their settings schema. Through api.dzbuild.app a successful answer is cached for 30 seconds per key and query string; add any query string to read past it.",
        "security": [
          {
            "dzOAuth": [
              "store:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The home page layout.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HomeLayout"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "put": {
        "operationId": "replaceStoreHomeLayout",
        "tags": [
          "Store"
        ],
        "summary": "Replace the home page sections",
        "description": "Replaces the whole layout with the sent list (at most 25 rows, an empty list clears the page). A row with an id keeps that section; a row without one creates a section. Every write answers the whole layout in render order, is live at once and returns change_id (null when nothing changed); POST /v1/changes/{id}/undo puts the previous layout back and answers 409 layout_changed when the page changed again since. 403 codes: forbidden (scope), plan_required (more sections than the plan allows). 409 code: write_conflict (another write landed first, or the sent version is not the current one; read and retry). 422 codes: invalid_section_type, invalid_settings (error.fields lists every refused setting of the first refused section), limit_reached, idempotency_key_reuse. Sending the layout read from GET unchanged answers change_id null. Home layout writes are limited to 30 a minute and 5 at once per store (429 rate_limited, too_many_concurrent).",
        "security": [
          {
            "dzOAuth": [
              "store:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional on this PUT. When sent, a retry with the same key and the same body replays the stored answer for 24 hours; the same key with a different body is refused with 422 idempotency_key_reuse. Without it the call is never replayed.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64,
              "pattern": "^[A-Za-z0-9_:.-]+$"
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HomeLayoutReplaceInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The layout after the replace.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HomeLayoutWriteResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/store/home-layout/reorder": {
      "post": {
        "operationId": "reorderStoreHomeSections",
        "tags": [
          "Store"
        ],
        "summary": "Reorder the home page sections",
        "description": "Sets the render order. ids must list every section of the page exactly once: a missing or repeated id answers 422 invalid_order, an id that is not on the page 404 section_not_found. Every write answers the whole layout in render order, is live at once and returns change_id (null when nothing changed); POST /v1/changes/{id}/undo puts the previous layout back and answers 409 layout_changed when the page changed again since. Home layout writes are limited to 30 a minute and 5 at once per store (429 rate_limited, too_many_concurrent).",
        "security": [
          {
            "dzOAuth": [
              "store:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HomeLayoutReorderInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The layout in its new order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HomeLayoutWriteResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/store/home-layout/sections": {
      "post": {
        "operationId": "addStoreHomeSection",
        "tags": [
          "Store"
        ],
        "summary": "Add a home page section",
        "description": "Adds one section of a type listed by GET /v1/store/home-layout, at position (0 = top) or at the end. Unsent settings take the type defaults. A section whose content is unset (category 0 or a category without products, a banner without an image, image-with-text without an image or any text, rich-text, testimonials, faq or video left empty) is stored and answers 2xx, but buyers do not see it until it is filled. Every write answers the whole layout in render order, is live at once and returns change_id (null when nothing changed); POST /v1/changes/{id}/undo puts the previous layout back and answers 409 layout_changed when the page changed again since. 403 codes: forbidden (scope), plan_required (more sections than the plan allows). 409 code: write_conflict (another write landed first; read and retry). 422 codes: invalid_section_type, invalid_settings (error.fields lists every path), limit_reached, idempotency_key_reuse. Home layout writes are limited to 30 a minute and 5 at once per store (429 rate_limited, too_many_concurrent).",
        "security": [
          {
            "dzOAuth": [
              "store:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HomeSectionInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The section was added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HomeLayoutWriteResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/store/home-layout/sections/{id}": {
      "patch": {
        "operationId": "updateStoreHomeSection",
        "tags": [
          "Store"
        ],
        "summary": "Update a home page section",
        "description": "Changes one section. Sent settings are merged over what is stored; replace true starts again from the type defaults. is_active false hides the section without deleting it. Send settings, is_active or both, otherwise 422 no_changes. Every write answers the whole layout in render order, is live at once and returns change_id (null when nothing changed); POST /v1/changes/{id}/undo puts the previous layout back and answers 409 layout_changed when the page changed again since. 403 code: forbidden (scope). 409 code: write_conflict (another write landed first; read and retry). 422 codes: invalid_settings (error.fields lists every path), idempotency_key_reuse. 400 bad_request when is_active or replace is not a boolean. 404 code: section_not_found. Home layout writes are limited to 30 a minute and 5 at once per store (429 rate_limited, too_many_concurrent).",
        "security": [
          {
            "dzOAuth": [
              "store:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Home section id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HomeSectionUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The layout after the update.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HomeLayoutWriteResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "delete": {
        "operationId": "deleteStoreHomeSection",
        "tags": [
          "Store"
        ],
        "summary": "Delete a home page section",
        "description": "Removes one section. Undo brings it back with a new id. Every write answers the whole layout in render order, is live at once and returns change_id (null when nothing changed); POST /v1/changes/{id}/undo puts the previous layout back and answers 409 layout_changed when the page changed again since. 404 code: section_not_found. Home layout writes are limited to 30 a minute and 5 at once per store (429 rate_limited, too_many_concurrent).",
        "security": [
          {
            "dzOAuth": [
              "store:write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Home section id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The layout after the delete.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HomeLayoutWriteResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/store/home-sections": {
      "get": {
        "operationId": "getStoreHomeSections",
        "tags": [
          "Store"
        ],
        "summary": "Get the home page section settings",
        "description": "Returns the theme key and the stored home page section settings (sliders, banners, category columns, testimonials, section order). Only what has been saved is returned; a store that never saved any gets an empty JSON array `[]`. Through `api.dzbuild.app` a successful answer is cached for 30 seconds per key and query string (`X-Cache: HIT` or `MISS`), so a change can take up to 30 seconds to show. Needs a key on an active Enterprise plan (first-party assistant keys excepted) that is enrolled while the API is in pilot mode; otherwise the call returns 403 `forbidden`.",
        "security": [
          {
            "dzOAuth": [
              "store:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Theme key and stored section settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreHomeSections"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "patch": {
        "operationId": "updateStoreHomeSections",
        "tags": [
          "Store"
        ],
        "summary": "Update home page section settings",
        "description": "Merges the keys present in the body into the stored home page section settings; keys you leave out keep their value. At least one accepted key is required. The change is recorded in the change log and can be undone. Requires an `Idempotency-Key` header (at most 64 characters of letters, digits, `_`, `-`, `:` and `.`); a missing or malformed key returns 400 `bad_request`, and reusing a key for a different body returns 422 `idempotency_key_reuse`. A repeat with the same key and body replays the stored answer for 24 hours with `Idempotency-Replay: 1`. Error codes on 422: `no_writable_fields`, `invalid_category`, `invalid_url`, `invalid_value`, `idempotency_key_reuse`. 404 `store_not_found` if the store was deleted.",
        "security": [
          {
            "dzOAuth": [
              "store:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreHomeSectionsUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Keys written and the full merged settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreHomeSectionsUpdateResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/store/theme": {
      "post": {
        "operationId": "setStoreTheme",
        "tags": [
          "Store"
        ],
        "summary": "Switch the storefront theme",
        "description": "Switches the store to another storefront theme. `GET /v1/themes` lists the keys and whether this store may use each one. Switching to `digital` also turns the store into a digital-products store and replaces colours that still hold one of the stock light defaults with the dark palette; colours the merchant chose are kept. The change is recorded in the change log and can be undone. Requires an `Idempotency-Key` header (at most 64 characters of letters, digits, `_`, `-`, `:` and `.`); a missing or malformed key returns 400 `bad_request`, and reusing a key for a different body returns 422 `idempotency_key_reuse`. A repeat with the same key and body replays the stored answer for 24 hours with `Idempotency-Replay: 1`. Errors: 422 `invalid_theme` (missing, longer than 50 characters or not letters, digits, `_` and `-`), 404 `theme_not_found`, 403 `plan_required` (the plan does not include the theme), 503 `column_missing` while the feature is being rolled out, 404 `store_not_found`.",
        "security": [
          {
            "dzOAuth": [
              "store:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreThemeSelect"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The theme now in use.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreThemeResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/store/variant-style": {
      "post": {
        "operationId": "setStoreVariantStyle",
        "tags": [
          "Store"
        ],
        "summary": "Switch the product variant picker style",
        "description": "Switches how variant choices (size, colour and so on) are drawn on product pages. An unknown or inactive style is refused rather than replaced with the default. The change is recorded in the change log and can be undone. Requires an `Idempotency-Key` header (at most 64 characters of letters, digits, `_`, `-`, `:` and `.`); a missing or malformed key returns 400 `bad_request`, and reusing a key for a different body returns 422 `idempotency_key_reuse`. A repeat with the same key and body replays the stored answer for 24 hours with `Idempotency-Replay: 1`. Errors: 422 `invalid_style` (missing or longer than 50 characters), 404 `style_not_found`, 403 `plan_required`, 503 `column_missing` while the feature is being rolled out, 404 `store_not_found`.",
        "security": [
          {
            "dzOAuth": [
              "store:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreVariantStyleSelect"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The variant style now in use.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StoreVariantStyleResult"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotency-Replay": {
                "$ref": "#/components/headers/IdempotencyReplay"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/themes": {
      "get": {
        "operationId": "listThemes",
        "tags": [
          "Store"
        ],
        "summary": "List storefront themes",
        "description": "Returns every storefront theme with its title in the store language, plan requirement, colour mode and whether this store may use it, plus the key of the theme in use. Full list in `data.items`, no pagination. Needs a key on an active Enterprise plan (first-party assistant keys excepted) that is enrolled while the API is in pilot mode; otherwise the call returns 403 `forbidden`.",
        "security": [
          {
            "dzOAuth": [
              "store:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "All themes with a per-store verdict.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ThemeList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/whatsapp/balance": {
      "get": {
        "operationId": "getWhatsAppBalance",
        "tags": [
          "WhatsApp"
        ],
        "summary": "Get the WhatsApp wallet balance and message counts",
        "description": "Returns the number of messages left in the store's WhatsApp wallet, whether the balance is low (under 50 messages), whether the WhatsApp sender addon is active, and the message counts of the last 30 days by delivery status with the credits used since the first of the month. A store that never bought credits has a balance of 0.",
        "security": [
          {
            "dzOAuth": [
              "whatsapp:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The wallet state.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WhatsAppBalance"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/whatsapp/messages": {
      "get": {
        "operationId": "listWhatsAppMessages",
        "tags": [
          "WhatsApp"
        ],
        "summary": "List the WhatsApp messages sent to buyers",
        "description": "Lists the store's WhatsApp order messages, newest first, both the automatic ones (`source` auto) and the ones queued through the API (`source` api). The buyer phone number is never returned. Cursor list: data.items, data.next_cursor and data.has_more.",
        "security": [
          {
            "dzOAuth": [
              "whatsapp:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "order_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[0-9]+$"
            },
            "description": "Only messages for this order id. A value that is not all digits returns 400."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of messages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WhatsAppMessageList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/whatsapp/templates": {
      "get": {
        "operationId": "listWhatsAppTemplates",
        "tags": [
          "WhatsApp"
        ],
        "summary": "List the WhatsApp order message templates",
        "description": "Returns the platform-approved order message templates with their Arabic and French text, an example of the filled parameters and the approval status of each language. The status is the last one the platform read from WhatsApp (refreshed at most every 10 minutes while messages are being sent); UNKNOWN means it has not been read yet. This call never contacts WhatsApp. Pass `key` as `template` to POST /v1/orders/{id}/whatsapp; `toggle` is the automatic-message switch in the addon settings that owns the template. Full list in data.items.",
        "security": [
          {
            "dzOAuth": [
              "whatsapp:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The template catalogue.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WhatsAppTemplateList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/whoami": {
      "get": {
        "operationId": "whoami",
        "tags": [
          "Connection"
        ],
        "summary": "Describe the calling key",
        "description": "Returns the key id, the store it belongs to, its type, the rate-limit tier in force (it follows the store current plan), whether it is pilot-enrolled and its scopes. Needs no scope, only a valid key. Needs a key on an active Enterprise plan (first-party assistant keys excepted) that is enrolled while the API is in pilot mode; otherwise the call returns 403 `forbidden`.",
        "security": [
          {
            "dzOAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "The resolved key context.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WhoAmI"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "x-dz-presets": [
          "cloudflare-basic",
          "cloudflare-catalog",
          "cloudflare-orders"
        ]
      }
    },
    "/v1/wilayas": {
      "get": {
        "operationId": "listWilayas",
        "tags": [
          "Shipping"
        ],
        "summary": "List wilayas",
        "description": "Lists the wilayas this store can deliver to, following its wilaya mode: 1 to 58 in the courier-compatible mode, 1 to 69 in 69-wilaya mode. Names come in Arabic, French and English. Full list in `data.wilayas`, no pagination. Needs a key on an active Enterprise plan (first-party assistant keys excepted) that is enrolled while the API is in pilot mode; otherwise the call returns 403 `forbidden`.",
        "security": [
          {
            "dzOAuth": [
              "shipping:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Wilayas for this store mode.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WilayaList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/v1/wilayas/{id}/communes": {
      "get": {
        "operationId": "listWilayaCommunes",
        "tags": [
          "Shipping"
        ],
        "summary": "List the communes of a wilaya",
        "description": "Lists the communes of one wilaya, sorted by French name. Any wilaya from 1 to 69 is answered regardless of the store wilaya mode. Full list in `data.communes`, no pagination. 400 `bad_request` when the id is not digits, 404 `not_found` when the wilaya does not exist. Needs a key on an active Enterprise plan (first-party assistant keys excepted) that is enrolled while the API is in pilot mode; otherwise the call returns 403 `forbidden`.",
        "security": [
          {
            "dzOAuth": [
              "shipping:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Wilaya id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/xRequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Communes of the wilaya.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CommuneList"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AnalyticsCharts": {
        "type": "object",
        "required": [
          "revenue_over_time",
          "orders_by_hour",
          "orders_by_status",
          "top_products",
          "visitors_over_time",
          "devices",
          "traffic_sources",
          "top_wilayas"
        ],
        "properties": {
          "revenue_over_time": {
            "type": "array",
            "description": "One point per bucket, future buckets trimmed. revenue counts delivered orders only and is present only for the store owner.",
            "items": {
              "type": "object",
              "required": [
                "label",
                "orders"
              ],
              "properties": {
                "label": {
                  "type": "string",
                  "description": "HH:00, MM/DD or Mon YYYY depending on group_by."
                },
                "orders": {
                  "type": "integer"
                },
                "revenue": {
                  "type": "integer"
                }
              }
            }
          },
          "orders_by_hour": {
            "type": "array",
            "description": "24 entries, hour 00:00 to 23:00, across the whole range.",
            "items": {
              "type": "object",
              "required": [
                "hour",
                "orders",
                "impressions"
              ],
              "properties": {
                "hour": {
                  "type": "string"
                },
                "orders": {
                  "type": "integer"
                },
                "impressions": {
                  "type": "integer"
                }
              }
            }
          },
          "orders_by_status": {
            "type": "object",
            "required": [
              "pending",
              "confirmed",
              "processing",
              "shipped",
              "delivered",
              "cancelled",
              "returned"
            ],
            "properties": {
              "pending": {
                "type": "integer"
              },
              "confirmed": {
                "type": "integer"
              },
              "processing": {
                "type": "integer"
              },
              "shipped": {
                "type": "integer"
              },
              "delivered": {
                "type": "integer"
              },
              "cancelled": {
                "type": "integer"
              },
              "returned": {
                "type": "integer"
              }
            }
          },
          "top_products": {
            "type": "array",
            "description": "Up to 10 products by quantity sold, cancelled and returned orders excluded. revenue is present only for the store owner.",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name",
                "image",
                "qty_sold",
                "views"
              ],
              "properties": {
                "id": {
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                },
                "image": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Primary image URL."
                },
                "qty_sold": {
                  "type": "integer"
                },
                "revenue": {
                  "type": "number"
                },
                "views": {
                  "type": "integer",
                  "description": "Distinct visitors on the product in the range."
                }
              }
            }
          },
          "visitors_over_time": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "label",
                "page_views",
                "unique_visitors",
                "orders"
              ],
              "properties": {
                "label": {
                  "type": "string"
                },
                "page_views": {
                  "type": "integer"
                },
                "unique_visitors": {
                  "type": "integer"
                },
                "orders": {
                  "type": "integer"
                }
              }
            }
          },
          "devices": {
            "type": [
              "object",
              "array"
            ],
            "description": "Map of device type to count and percent. Serialised as an empty array when there were no visits.",
            "additionalProperties": {
              "type": "object",
              "required": [
                "count",
                "percent"
              ],
              "properties": {
                "count": {
                  "type": "integer"
                },
                "percent": {
                  "type": "number"
                }
              }
            }
          },
          "traffic_sources": {
            "type": "array",
            "description": "Up to 6 sources, busiest first.",
            "items": {
              "type": "object",
              "required": [
                "source",
                "count",
                "percent"
              ],
              "properties": {
                "source": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "count": {
                  "type": "integer"
                },
                "percent": {
                  "type": "number"
                }
              }
            }
          },
          "top_wilayas": {
            "type": "array",
            "description": "Up to 10 wilayas by order count. wilaya is the Arabic name. revenue is present only for the store owner.",
            "items": {
              "type": "object",
              "required": [
                "wilaya",
                "orders"
              ],
              "properties": {
                "wilaya": {
                  "type": "string"
                },
                "orders": {
                  "type": "integer"
                },
                "revenue": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "AnalyticsKpi": {
        "type": "object",
        "required": [
          "value",
          "change",
          "previous"
        ],
        "properties": {
          "value": {
            "type": "number"
          },
          "change": {
            "type": "number",
            "description": "Percent change against the comparison period, rounded; 100 when the previous value was 0 and the current one is not."
          },
          "previous": {
            "type": "number"
          }
        }
      },
      "AnalyticsKpis": {
        "type": "object",
        "required": [
          "total_orders",
          "delivered_orders",
          "cancelled_orders",
          "total_visitors",
          "page_views",
          "conversion_rate",
          "new_customers"
        ],
        "description": "total_revenue, total_profit and avg_order_value are present only when the key belongs to the store owner.",
        "properties": {
          "total_orders": {
            "$ref": "#/components/schemas/AnalyticsKpi"
          },
          "delivered_orders": {
            "$ref": "#/components/schemas/AnalyticsKpi"
          },
          "cancelled_orders": {
            "$ref": "#/components/schemas/AnalyticsKpi"
          },
          "total_revenue": {
            "$ref": "#/components/schemas/AnalyticsKpi"
          },
          "total_profit": {
            "$ref": "#/components/schemas/AnalyticsKpi"
          },
          "avg_order_value": {
            "$ref": "#/components/schemas/AnalyticsKpi"
          },
          "total_visitors": {
            "$ref": "#/components/schemas/AnalyticsKpi"
          },
          "page_views": {
            "$ref": "#/components/schemas/AnalyticsKpi"
          },
          "conversion_rate": {
            "$ref": "#/components/schemas/AnalyticsKpi"
          },
          "new_customers": {
            "$ref": "#/components/schemas/AnalyticsKpi"
          }
        }
      },
      "AnalyticsPeriod": {
        "type": "object",
        "required": [
          "from",
          "to",
          "prev_from",
          "prev_to",
          "label",
          "group_by"
        ],
        "properties": {
          "from": {
            "type": "string",
            "description": "Date and time as `YYYY-MM-DD HH:MM:SS`, store server local time."
          },
          "to": {
            "type": "string",
            "description": "Date and time as `YYYY-MM-DD HH:MM:SS`, store server local time."
          },
          "prev_from": {
            "type": "string",
            "description": "Start of the comparison period, same length just before `from`."
          },
          "prev_to": {
            "type": "string",
            "description": "End of the comparison period."
          },
          "label": {
            "type": "string",
            "enum": [
              "today",
              "yesterday",
              "7d",
              "30d",
              "this_month",
              "last_month",
              "this_year",
              "custom"
            ]
          },
          "group_by": {
            "type": "string",
            "enum": [
              "hour",
              "day",
              "month"
            ],
            "description": "Bucket size of the time series."
          }
        }
      },
      "AnalyticsReport": {
        "type": "object",
        "required": [
          "period",
          "kpis",
          "charts"
        ],
        "properties": {
          "period": {
            "$ref": "#/components/schemas/AnalyticsPeriod"
          },
          "kpis": {
            "$ref": "#/components/schemas/AnalyticsKpis"
          },
          "charts": {
            "$ref": "#/components/schemas/AnalyticsCharts"
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "required": [
          "key_id",
          "type",
          "name",
          "scopes",
          "rate_limit_tier",
          "pilot",
          "status",
          "last_used_at",
          "last_used_ip",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "key_id": {
            "type": "string",
            "description": "24 characters including the `dzpk_live_` or `dzpub_live_` prefix."
          },
          "type": {
            "type": "string",
            "enum": [
              "platform",
              "public"
            ]
          },
          "name": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rate_limit_tier": {
            "type": "string",
            "enum": [
              "free",
              "pro",
              "pro_plus",
              "enterprise",
              "custom"
            ]
          },
          "pilot": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "suspended"
            ]
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Timestamp as `YYYY-MM-DD HH:MM:SS`."
          },
          "last_used_ip": {
            "type": [
              "string",
              "null"
            ],
            "description": "IPv4 or IPv6 address of the last caller."
          },
          "created_at": {
            "type": "string",
            "description": "Timestamp as `YYYY-MM-DD HH:MM:SS`."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Timestamp as `YYYY-MM-DD HH:MM:SS`. A key past this time stops authenticating."
          }
        }
      },
      "ApiKeyCreated": {
        "type": "object",
        "required": [
          "key_id",
          "bearer_token",
          "signing_secret",
          "note"
        ],
        "properties": {
          "key_id": {
            "type": "string"
          },
          "bearer_token": {
            "type": [
              "string",
              "null"
            ],
            "description": "Platform keys: `{key_id}.{48 hex characters}`, the value for `Authorization: Bearer`. Null for public keys."
          },
          "signing_secret": {
            "type": "string",
            "description": "64 hex characters, used to sign public-key requests."
          },
          "note": {
            "type": "string"
          }
        }
      },
      "ApiKeyInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "platform",
              "public"
            ],
            "default": "platform"
          },
          "name": {
            "type": "string",
            "default": "default",
            "description": "Label. Longer values are cut to 100 bytes of UTF-8."
          }
        }
      },
      "ApiKeyList": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            }
          }
        }
      },
      "ApiKeyRevoked": {
        "type": "object",
        "required": [
          "revoked",
          "key_id"
        ],
        "properties": {
          "revoked": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "key_id": {
            "type": "string"
          }
        }
      },
      "Category": {
        "type": "object",
        "description": "A store category. Categories are at most two levels deep: a top-level category and its subcategories.",
        "required": [
          "id",
          "name",
          "slug",
          "description",
          "image",
          "parent_id",
          "show_subcategories",
          "sort_order",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "maxLength": 100
          },
          "slug": {
            "type": "string",
            "maxLength": 100,
            "description": "Generated from the name and unique within the store. It changes when the name changes."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "image": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Full image URL, or null. The image is set in the dashboard; the API cannot upload or change it."
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Id of the parent category, or null for a top-level category."
          },
          "show_subcategories": {
            "type": "boolean",
            "description": "Always true for a subcategory, and for every category when the store shows subcategories only inside their parent."
          },
          "sort_order": {
            "type": "integer"
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "active",
              "inactive",
              null
            ]
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Date-time in the `YYYY-MM-DD HH:MM:SS` form, Africa/Algiers local time."
          }
        }
      },
      "CategoryChild": {
        "type": "object",
        "required": [
          "id",
          "name",
          "slug",
          "sort_order",
          "status"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "sort_order": {
            "type": "integer"
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "active",
              "inactive",
              null
            ]
          }
        }
      },
      "CategoryDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Category"
          },
          {
            "type": "object",
            "required": [
              "children",
              "product_count"
            ],
            "properties": {
              "children": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CategoryChild"
                },
                "description": "Subcategories, ordered by sort_order then id."
              },
              "product_count": {
                "type": "integer",
                "description": "Number of products linked to this category."
              }
            }
          }
        ]
      },
      "CategoryInput": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Trimmed; 1 to 100 characters. Changing it regenerates the slug."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Trimmed; an empty string is stored as null."
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Id of a top-level category in this store. null or 0 makes it top-level. The parent must not itself be a subcategory, a category cannot be its own parent, and a category that has subcategories cannot become one."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive"
            ],
            "default": "active"
          },
          "show_subcategories": {
            "type": "boolean",
            "description": "Ignored (forced to true) for a subcategory or when the store shows subcategories only inside their parent.",
            "default": true
          }
        }
      },
      "CategoryListItem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Category"
          },
          {
            "type": "object",
            "required": [
              "product_count"
            ],
            "properties": {
              "product_count": {
                "type": "integer",
                "description": "Number of products linked to this category."
              }
            }
          }
        ]
      },
      "CategoryReorderInput": {
        "type": "object",
        "required": [
          "categories"
        ],
        "properties": {
          "categories": {
            "type": "array",
            "minItems": 1,
            "description": "Categories in the new order. Array position sets sort_order (1, 2, 3 ...) unless an item carries its own sort_order. Each id may appear once and must belong to this store. Categories left out keep their current sort_order.",
            "items": {
              "oneOf": [
                {
                  "type": "object",
                  "required": [
                    "id"
                  ],
                  "properties": {
                    "id": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "sort_order": {
                      "type": "integer"
                    }
                  }
                },
                {
                  "type": "integer",
                  "minimum": 1,
                  "description": "A bare category id."
                }
              ]
            }
          }
        }
      },
      "CategoryReorderResult": {
        "type": "object",
        "required": [
          "reordered",
          "categories"
        ],
        "properties": {
          "reordered": {
            "type": "integer",
            "description": "Number of categories updated."
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "sort_order"
              ],
              "properties": {
                "id": {
                  "type": "integer"
                },
                "sort_order": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "CategoryUpdate": {
        "type": "object",
        "description": "Partial update: only the keys you send are changed.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Trimmed; 1 to 100 characters. Changing it regenerates the slug."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Trimmed; an empty string is stored as null."
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Id of a top-level category in this store. null or 0 makes it top-level. The parent must not itself be a subcategory, a category cannot be its own parent, and a category that has subcategories cannot become one."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive"
            ]
          },
          "show_subcategories": {
            "type": "boolean",
            "description": "Ignored (forced to true) for a subcategory or when the store shows subcategories only inside their parent."
          }
        }
      },
      "Change": {
        "type": "object",
        "required": [
          "id",
          "entity",
          "entity_id",
          "action",
          "summary",
          "undone_at",
          "created_at",
          "undone"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "entity": {
            "$ref": "#/components/schemas/ChangeEntity"
          },
          "entity_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the changed item; for shipping, `rates` or `settings`. Null for whole-store settings."
          },
          "action": {
            "type": "string",
            "enum": [
              "create",
              "update",
              "delete"
            ]
          },
          "summary": {
            "type": [
              "string",
              "null"
            ],
            "description": "Short description of the change."
          },
          "undone_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Timestamp as `YYYY-MM-DD HH:MM:SS`."
          },
          "created_at": {
            "type": "string",
            "description": "Timestamp as `YYYY-MM-DD HH:MM:SS`."
          },
          "undone": {
            "type": "boolean"
          }
        }
      },
      "ChangeDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Change"
          },
          {
            "type": "object",
            "required": [
              "key_id",
              "undone_by_id",
              "before",
              "after"
            ],
            "properties": {
              "key_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Key that made the change."
              },
              "undone_by_id": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "The change that undid this one."
              },
              "before": {
                "type": [
                  "object",
                  "array",
                  "null"
                ],
                "description": "Prior state of the touched fields; null for a create. Shape depends on the entity."
              },
              "after": {
                "type": [
                  "object",
                  "array",
                  "null"
                ],
                "description": "Applied state; null when not recorded (a courier rate sync records only `before`). Shape depends on the entity."
              }
            }
          }
        ]
      },
      "ChangeEntity": {
        "type": "string",
        "enum": [
          "store.design",
          "store.settings",
          "store.theme",
          "store.home_sections",
          "store.home_layout",
          "pixels",
          "shipping.rates",
          "shipping.settings",
          "lp.section",
          "lp.page",
          "promo_code",
          "category",
          "stock"
        ]
      },
      "ChangeList": {
        "type": "object",
        "required": [
          "items",
          "next_cursor",
          "has_more"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Change"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "ChangeUndoResult": {
        "type": "object",
        "required": [
          "undone",
          "change_id",
          "entity",
          "undo_change_id"
        ],
        "properties": {
          "undone": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "change_id": {
            "type": "integer"
          },
          "entity": {
            "$ref": "#/components/schemas/ChangeEntity"
          },
          "undo_change_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The change recording this undo; null if it could not be recorded."
          }
        }
      },
      "Commune": {
        "type": "object",
        "required": [
          "id",
          "wilaya_id",
          "name_ar",
          "name_fr"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "wilaya_id": {
            "type": "integer"
          },
          "name_ar": {
            "type": [
              "string",
              "null"
            ]
          },
          "name_fr": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CommuneList": {
        "type": "object",
        "required": [
          "wilaya_id",
          "count",
          "communes"
        ],
        "properties": {
          "wilaya_id": {
            "type": "integer"
          },
          "count": {
            "type": "integer"
          },
          "communes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Commune"
            }
          }
        }
      },
      "Connection": {
        "type": "object",
        "required": [
          "connection_id",
          "client_name",
          "active_store_id",
          "stores"
        ],
        "properties": {
          "connection_id": {
            "type": "integer"
          },
          "client_name": {
            "type": "string",
            "description": "Name of the assistant app that holds the connection."
          },
          "active_store_id": {
            "type": "integer"
          },
          "stores": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name",
                "slug"
              ],
              "properties": {
                "id": {
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                },
                "slug": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ConnectionActiveStoreInput": {
        "type": "object",
        "required": [
          "store_id"
        ],
        "properties": {
          "store_id": {
            "type": "integer",
            "minimum": 1
          }
        }
      },
      "Customer": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CustomerSummary"
          },
          {
            "type": "object",
            "required": [
              "address",
              "notes",
              "fraud_score"
            ],
            "properties": {
              "address": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "notes": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Merchant-private comment set in the dashboard."
              },
              "fraud_score": {
                "type": "integer"
              }
            }
          }
        ]
      },
      "CustomerList": {
        "type": "object",
        "required": [
          "items",
          "next_cursor",
          "has_more"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerSummary"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "CustomerOrderList": {
        "type": "object",
        "required": [
          "items",
          "next_cursor",
          "has_more"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerOrderSummary"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "CustomerOrderSummary": {
        "type": "object",
        "required": [
          "id",
          "order_number",
          "status",
          "payment_status",
          "total",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "order_number": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "payment_status": {
            "type": [
              "string",
              "null"
            ]
          },
          "total": {
            "type": "number"
          },
          "created_at": {
            "type": "string",
            "description": "Date and time as `YYYY-MM-DD HH:MM:SS`, store server local time."
          }
        }
      },
      "CustomerSummary": {
        "type": "object",
        "required": [
          "id",
          "first_name",
          "last_name",
          "phone",
          "email",
          "wilaya_id",
          "commune",
          "total_orders",
          "total_spent",
          "is_banned",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "wilaya_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "commune": {
            "type": [
              "string",
              "null"
            ]
          },
          "total_orders": {
            "type": "integer",
            "description": "Stored counter, not a live count. Landing-page and dashboard orders raise it; storefront and API orders do not, and status changes never lower it."
          },
          "total_spent": {
            "type": "number",
            "description": "Stored counter in DZD with the same limits as total_orders."
          },
          "is_banned": {
            "type": "boolean",
            "description": "True when the merchant blacklisted this customer."
          },
          "created_at": {
            "type": "string",
            "description": "Date and time as `YYYY-MM-DD HH:MM:SS`, store server local time."
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Date and time as `YYYY-MM-DD HH:MM:SS`, store server local time."
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Error envelope returned with every 4xx and 5xx answer. A method a path does not support answers 405 method_not_allowed with this envelope.",
        "required": [
          "error",
          "meta"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable code, for example bad_request, forbidden, not_found or rate_limited. Operations name the codes they can return."
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation. Do not parse it."
              },
              "retry_after": {
                "type": "integer",
                "description": "Seconds to wait before retrying; also sent as the Retry-After header."
              },
              "fields": {
                "type": "array",
                "description": "invalid_settings and invalid_section_type: every rejected value, with the path in the request body.",
                "items": {
                  "type": "object",
                  "required": [
                    "path",
                    "code"
                  ],
                  "properties": {
                    "path": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  }
                }
              },
              "limit": {
                "type": "integer",
                "description": "limit_reached only: how many sections the rule allows."
              },
              "cap": {
                "type": "integer",
                "description": "plan_required from the home layout only: sections the plan allows."
              },
              "version": {
                "type": "string",
                "description": "write_conflict from the home layout only: the current layout version, to send back on the retry."
              },
              "sections": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/HomeSection"
                },
                "description": "write_conflict from the home layout only: the current sections in render order."
              }
            },
            "additionalProperties": true
          },
          "meta": {
            "$ref": "#/components/schemas/Meta"
          }
        }
      },
      "HomeLayout": {
        "type": "object",
        "required": [
          "theme",
          "rendered",
          "max_sections",
          "cap",
          "version",
          "sections",
          "types"
        ],
        "properties": {
          "theme": {
            "type": "string"
          },
          "rendered": {
            "type": "boolean",
            "description": "False when the current theme does not show stored sections; they are kept for a theme that does. On a section theme, stored sections show only while one product-grid section is visible."
          },
          "max_sections": {
            "type": "integer",
            "const": 25
          },
          "cap": {
            "type": "integer",
            "description": "Sections the plan allows on this store."
          },
          "version": {
            "type": "string",
            "description": "Fingerprint of the stored layout; send it back as version on PUT to refuse a layout that changed since this read."
          },
          "sections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HomeSection"
            }
          },
          "types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HomeSectionType"
            }
          }
        }
      },
      "HomeLayoutReorderInput": {
        "type": "object",
        "required": [
          "ids"
        ],
        "properties": {
          "ids": {
            "type": "array",
            "maxItems": 25,
            "items": {
              "type": "integer"
            },
            "description": "Every section id of the page, in the new order."
          }
        }
      },
      "HomeLayoutReplaceInput": {
        "type": "object",
        "required": [
          "sections"
        ],
        "properties": {
          "sections": {
            "type": "array",
            "maxItems": 25,
            "items": {
              "type": "object",
              "required": [
                "type"
              ],
              "properties": {
                "id": {
                  "type": "integer",
                  "description": "Existing section to keep; omit to create one."
                },
                "type": {
                  "type": "string"
                },
                "settings": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Setting id to value, as described by the type settings_schema."
                },
                "is_active": {
                  "type": "boolean",
                  "default": true
                }
              }
            }
          },
          "version": {
            "type": "string",
            "description": "The version read from GET /v1/store/home-layout; when sent, a layout that changed since answers 409 write_conflict and nothing is written."
          }
        }
      },
      "HomeLayoutWriteResult": {
        "type": "object",
        "required": [
          "sections",
          "version",
          "change_id",
          "rendered"
        ],
        "properties": {
          "section": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/HomeSection"
              },
              {
                "type": "null"
              }
            ],
            "description": "The added or updated section (add and update only)."
          },
          "deleted": {
            "type": "boolean",
            "description": "Delete only."
          },
          "id": {
            "type": "integer",
            "description": "Delete only: the removed section id."
          },
          "sections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HomeSection"
            }
          },
          "version": {
            "type": "string",
            "description": "Fingerprint of the layout after this write."
          },
          "change_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Change log id for POST /v1/changes/{id}/undo; null when nothing changed."
          },
          "rendered": {
            "type": "boolean"
          }
        }
      },
      "HomeSection": {
        "description": "A section whose content is unset (category 0 or a category without products, a banner without an image, image-with-text without an image or any text, rich-text, testimonials, faq or video left empty) is stored and answers 2xx, but buyers do not see it until it is filled.",
        "type": "object",
        "required": [
          "id",
          "type",
          "settings",
          "is_active",
          "available"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "type": {
            "type": "string"
          },
          "settings": {
            "type": "object",
            "additionalProperties": true,
            "description": "Setting id to value, as described by the type settings_schema."
          },
          "is_active": {
            "type": "boolean",
            "description": "False hides the section from buyers."
          },
          "available": {
            "type": "boolean",
            "description": "False when the current theme no longer has this section type; the row is kept as stored and still accepted in writes."
          }
        }
      },
      "HomeSectionInput": {
        "type": "object",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "maxLength": 40
          },
          "settings": {
            "type": "object",
            "additionalProperties": true,
            "description": "Setting id to value, as described by the type settings_schema."
          },
          "position": {
            "type": "integer",
            "minimum": 0,
            "maximum": 24,
            "description": "0 puts the section at the top; omitted adds it at the end."
          }
        }
      },
      "HomeSectionSetting": {
        "type": "object",
        "required": [
          "id",
          "type"
        ],
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "checkbox",
              "range",
              "select",
              "text",
              "textarea",
              "color",
              "link",
              "image",
              "category",
              "youtube"
            ],
            "description": "image: a dashboard upload path /uploads/banners/{store_id}/..., link: #anchor, /path, http(s)://, tel: or mailto: (a bare domain such as wa.me/213555 is stored with https://), up to 500 characters, youtube: a YouTube link (youtu.be, watch, shorts, embed, live) or 11-character id (the id is stored), color: #RRGGBB, category: an id from GET /v1/categories, 0 for none; every string setting also accepts \"\"."
          },
          "default": {
            "description": "Value used when the setting is not sent."
          },
          "min": {
            "type": "integer",
            "description": "Smallest number for range."
          },
          "max": {
            "type": "integer",
            "description": "Largest number for range, longest text in characters for text and textarea. A range value outside min..max is moved to the nearest end and text longer than max is cut; both answer 2xx, read the stored value in the reply."
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "label": {
            "type": "object",
            "properties": {
              "ar": {
                "type": "string"
              },
              "fr": {
                "type": "string"
              }
            }
          },
          "option_labels": {
            "type": "object",
            "description": "Option labels per language, in the same order as options.",
            "properties": {
              "ar": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "fr": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "HomeSectionType": {
        "type": "object",
        "required": [
          "type",
          "settings_schema"
        ],
        "properties": {
          "type": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{0,39}$"
          },
          "name": {
            "type": "object",
            "properties": {
              "ar": {
                "type": "string"
              },
              "fr": {
                "type": "string"
              }
            }
          },
          "description": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "ar": {
                "type": "string"
              },
              "fr": {
                "type": "string"
              }
            }
          },
          "icon": {
            "type": [
              "string",
              "null"
            ]
          },
          "limit": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Most sections of this type one page may hold."
          },
          "settings_schema": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HomeSectionSetting"
            }
          }
        }
      },
      "HomeSectionUpdate": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "settings": {
            "type": "object",
            "additionalProperties": true,
            "description": "Setting id to value, as described by the type settings_schema."
          },
          "is_active": {
            "type": "boolean"
          },
          "replace": {
            "type": "boolean",
            "default": false,
            "description": "True resets unsent settings to the type defaults. Any value other than true or false answers 400 bad_request."
          }
        }
      },
      "LandingPage": {
        "type": "object",
        "description": "A landing page with its SEO fields and section count.",
        "required": [
          "id",
          "title",
          "slug",
          "public_url",
          "status",
          "language",
          "product_id",
          "views",
          "is_purchased",
          "created_at",
          "updated_at",
          "meta_title",
          "meta_description",
          "section_count"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "description": "URL segment, unique within the store."
          },
          "public_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Live address of the page (the store's address followed by /landing/ and the slug). Null when the store has no address yet. Hand this value to people as is; do not build the address yourself."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "draft"
            ]
          },
          "language": {
            "type": "string",
            "description": "Page language: ar, fr or en."
          },
          "product_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Linked product, or null."
          },
          "views": {
            "type": "integer",
            "description": "Read-only view counter."
          },
          "is_purchased": {
            "type": "boolean",
            "description": "True when the page was bought outright. On the Free plan this is what makes it visible."
          },
          "created_at": {
            "type": "string",
            "description": "Date and time in the store's database clock, formatted YYYY-MM-DD HH:MM:SS."
          },
          "updated_at": {
            "type": "string",
            "description": "Date and time in the store's database clock, formatted YYYY-MM-DD HH:MM:SS."
          },
          "meta_title": {
            "type": [
              "string",
              "null"
            ],
            "description": "SEO title. Null when never set through the API."
          },
          "meta_description": {
            "type": [
              "string",
              "null"
            ],
            "description": "SEO description."
          },
          "section_count": {
            "type": "integer",
            "description": "Number of sections on the page, counted per request."
          }
        }
      },
      "LandingPageCheck": {
        "type": "object",
        "required": [
          "landing_page_id",
          "status",
          "product_id",
          "sections",
          "publishable",
          "blocked_by",
          "problems"
        ],
        "properties": {
          "landing_page_id": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "draft"
            ]
          },
          "product_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The page's own product."
          },
          "sections": {
            "type": "integer",
            "description": "Number of sections checked."
          },
          "publishable": {
            "type": "boolean",
            "description": "False when any problem is blocking."
          },
          "blocked_by": {
            "type": [
              "string",
              "null"
            ],
            "description": "Message of the first blocking problem, or null."
          },
          "problems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LandingPageProblem"
            }
          }
        }
      },
      "LandingPageDeleted": {
        "type": "object",
        "required": [
          "deleted",
          "id"
        ],
        "properties": {
          "deleted": {
            "type": "boolean",
            "const": true
          },
          "id": {
            "type": "integer"
          }
        }
      },
      "LandingPageGenerateInput": {
        "type": "object",
        "required": [
          "title",
          "product_id"
        ],
        "properties": {
          "title": {
            "type": "string",
            "minLength": 3,
            "description": "Trimmed and cut to 255 characters; at least 3 characters."
          },
          "description": {
            "type": "string",
            "description": "Optional brief for the page. Trimmed and cut to 2000 characters."
          },
          "product_id": {
            "type": "integer",
            "description": "An active product of this store with at least one image."
          },
          "language": {
            "type": "string",
            "enum": [
              "ar",
              "fr",
              "en"
            ],
            "default": "ar",
            "description": "Any other value is treated as ar."
          },
          "size": {
            "type": "string",
            "enum": [
              "medium",
              "tall"
            ],
            "default": "medium",
            "description": "Page length. It also sets the credit price. Any other value is treated as medium."
          }
        }
      },
      "LandingPageGeneration": {
        "type": "object",
        "required": [
          "task_id",
          "size",
          "credits_charged",
          "eta_seconds",
          "poll"
        ],
        "properties": {
          "task_id": {
            "type": "integer"
          },
          "size": {
            "type": "string",
            "enum": [
              "medium",
              "tall"
            ]
          },
          "credits_charged": {
            "type": "integer",
            "description": "AI credits taken from the store's balance for this run."
          },
          "eta_seconds": {
            "type": "integer",
            "description": "Always 120."
          },
          "poll": {
            "type": "string",
            "description": "Path to poll, /v1/landing-pages/generate/{task_id}."
          }
        }
      },
      "LandingPageGenerationStatus": {
        "type": "object",
        "required": [
          "task_id",
          "status",
          "landing_page_id",
          "current_step",
          "error"
        ],
        "properties": {
          "task_id": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "completed",
              "failed",
              "refunded"
            ]
          },
          "landing_page_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The generated page once it exists."
          },
          "current_step": {
            "type": "integer",
            "description": "Progress step of the run, 0 before the first step."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Failure reason when status is failed; timeout for a run that went past 10 minutes."
          }
        }
      },
      "LandingPageInput": {
        "type": "object",
        "required": [
          "title"
        ],
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Trimmed; 1 to 255 characters."
          },
          "slug": {
            "type": "string",
            "description": "Optional. Derived from the title when omitted. A slug sent here is stored without normalisation, then made unique within the store by appending -2, -3 and so on."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "draft"
            ],
            "default": "draft",
            "description": "Any other value is stored as draft. Creating a page as active skips the publish check that PATCH and publish run."
          },
          "language": {
            "type": "string",
            "enum": [
              "ar",
              "fr",
              "en"
            ],
            "default": "ar",
            "description": "Any other value is stored as ar."
          },
          "product_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Must be a product of this store."
          },
          "meta_title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 255 characters. An empty string is stored as null."
          },
          "meta_description": {
            "type": [
              "string",
              "null"
            ],
            "description": "An empty string is stored as null."
          }
        }
      },
      "LandingPageProblem": {
        "type": "object",
        "required": [
          "code",
          "severity",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "empty_page",
              "order_form_without_product",
              "section_product_not_found",
              "no_order_form",
              "multiple_order_forms",
              "variants_need_page_product"
            ],
            "description": "Blocking: empty_page, order_form_without_product, section_product_not_found. Warnings: no_order_form, multiple_order_forms, variants_need_page_product."
          },
          "severity": {
            "type": "string",
            "enum": [
              "blocking",
              "warning"
            ]
          },
          "section_id": {
            "type": "integer",
            "description": "Present on order_form_without_product and section_product_not_found."
          },
          "message": {
            "type": "string"
          }
        }
      },
      "LandingPageSection": {
        "type": "object",
        "description": "One section of a landing page. created_at and updated_at are present on the list, reorder and update responses and absent on the create and batch responses.",
        "required": [
          "id",
          "section_type",
          "sort_order",
          "settings"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "section_type": {
            "type": "string",
            "description": "One of the creatable types from GET /v1/landing-page-section-types. Pages built by AI generation can also hold other types, which the API lists but cannot create."
          },
          "sort_order": {
            "type": "integer",
            "description": "Render position, ascending."
          },
          "settings": {
            "type": "object",
            "additionalProperties": true,
            "description": "Type-specific settings. The keys for each type are the default_settings listed by GET /v1/landing-page-section-types."
          },
          "created_at": {
            "type": "string",
            "description": "Date and time in the store's database clock, formatted YYYY-MM-DD HH:MM:SS."
          },
          "updated_at": {
            "type": "string",
            "description": "Date and time in the store's database clock, formatted YYYY-MM-DD HH:MM:SS."
          }
        }
      },
      "LandingPageSectionBatchCreated": {
        "type": "object",
        "required": [
          "landing_page_id",
          "sections"
        ],
        "properties": {
          "landing_page_id": {
            "type": "integer"
          },
          "sections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LandingPageSection"
            }
          }
        }
      },
      "LandingPageSectionBatchInput": {
        "type": "object",
        "required": [
          "sections"
        ],
        "properties": {
          "sections": {
            "type": "array",
            "minItems": 1,
            "maxItems": 30,
            "description": "Sections to append, in order. All are validated before any is saved; one bad entry saves none.",
            "items": {
              "type": "object",
              "description": "Send type (or its alias section_type).",
              "anyOf": [
                {
                  "required": [
                    "type"
                  ]
                },
                {
                  "required": [
                    "section_type"
                  ]
                }
              ],
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "image",
                    "order_form",
                    "order_button",
                    "free_text",
                    "contact_button",
                    "countdown",
                    "fake_visitors",
                    "special_offer",
                    "price_display",
                    "product_offers",
                    "custom_form",
                    "image_carousel",
                    "announcement_bar",
                    "testimonials"
                  ]
                },
                "section_type": {
                  "type": "string",
                  "enum": [
                    "image",
                    "order_form",
                    "order_button",
                    "free_text",
                    "contact_button",
                    "countdown",
                    "fake_visitors",
                    "special_offer",
                    "price_display",
                    "product_offers",
                    "custom_form",
                    "image_carousel",
                    "announcement_bar",
                    "testimonials"
                  ],
                  "description": "Alias of type; type wins when both are sent."
                },
                "settings": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Optional. Merged over the type's default settings, so omitted keys keep their defaults. Nested objects merge key by key; lists (slides, items, offers, fields) replace wholesale. On order forms show_name, show_phone and show_wilaya are always stored as true. settings.product_id, when set, must be a product of this store. Lists are capped: slides at 20 entries, items at 30. The encoded settings may not exceed 262144 bytes. free_text content is cleaned to safe HTML."
                }
              }
            }
          }
        }
      },
      "LandingPageSectionCreated": {
        "type": "object",
        "required": [
          "landing_page_id",
          "section"
        ],
        "properties": {
          "landing_page_id": {
            "type": "integer"
          },
          "section": {
            "$ref": "#/components/schemas/LandingPageSection"
          }
        }
      },
      "LandingPageSectionDeleted": {
        "type": "object",
        "required": [
          "deleted",
          "id",
          "landing_page_id"
        ],
        "properties": {
          "deleted": {
            "type": "boolean",
            "const": true
          },
          "id": {
            "type": "integer",
            "description": "The deleted section id."
          },
          "landing_page_id": {
            "type": "integer"
          }
        }
      },
      "LandingPageSectionInput": {
        "type": "object",
        "description": "Send section_type (or its alias type).",
        "anyOf": [
          {
            "required": [
              "section_type"
            ]
          },
          {
            "required": [
              "type"
            ]
          }
        ],
        "properties": {
          "section_type": {
            "type": "string",
            "enum": [
              "image",
              "order_form",
              "order_button",
              "free_text",
              "contact_button",
              "countdown",
              "fake_visitors",
              "special_offer",
              "price_display",
              "product_offers",
              "custom_form",
              "image_carousel",
              "announcement_bar",
              "testimonials"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "order_form",
              "order_button",
              "free_text",
              "contact_button",
              "countdown",
              "fake_visitors",
              "special_offer",
              "price_display",
              "product_offers",
              "custom_form",
              "image_carousel",
              "announcement_bar",
              "testimonials"
            ],
            "description": "Alias of section_type; section_type wins when both are sent."
          },
          "settings": {
            "type": "object",
            "additionalProperties": true,
            "description": "Optional. Merged over the type's default settings, so omitted keys keep their defaults. Nested objects merge key by key; lists (slides, items, offers, fields) replace wholesale. On order forms show_name, show_phone and show_wilaya are always stored as true. settings.product_id, when set, must be a product of this store. Lists are capped: slides at 20 entries, items at 30. The encoded settings may not exceed 262144 bytes. free_text content is cleaned to safe HTML."
          }
        }
      },
      "LandingPageSectionList": {
        "type": "object",
        "required": [
          "landing_page_id",
          "sections"
        ],
        "properties": {
          "landing_page_id": {
            "type": "integer"
          },
          "sections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LandingPageSection"
            },
            "description": "Render order; at most 500 sections."
          }
        }
      },
      "LandingPageSectionReorderInput": {
        "type": "object",
        "description": "Send sections (or its alias section_ids): every section id of the page exactly once, in the new order.",
        "anyOf": [
          {
            "required": [
              "sections"
            ]
          },
          {
            "required": [
              "section_ids"
            ]
          }
        ],
        "properties": {
          "sections": {
            "type": "array",
            "items": {
              "type": [
                "integer",
                "string"
              ],
              "description": "Section id; a string of digits is accepted."
            }
          },
          "section_ids": {
            "type": "array",
            "items": {
              "type": [
                "integer",
                "string"
              ]
            },
            "description": "Alias of sections; sections wins when both are sent."
          }
        }
      },
      "LandingPageSectionResult": {
        "type": "object",
        "required": [
          "landing_page_id",
          "section"
        ],
        "properties": {
          "landing_page_id": {
            "type": "integer"
          },
          "section": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/LandingPageSection"
              },
              {
                "type": "null"
              }
            ],
            "description": "The section after the write. Null only when it falls outside the first 500 sections of the page."
          }
        }
      },
      "LandingPageSectionType": {
        "type": "object",
        "required": [
          "type",
          "default_settings"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "image",
              "order_form",
              "order_button",
              "free_text",
              "contact_button",
              "countdown",
              "fake_visitors",
              "special_offer",
              "price_display",
              "product_offers",
              "custom_form",
              "image_carousel",
              "announcement_bar",
              "testimonials"
            ]
          },
          "default_settings": {
            "type": "object",
            "additionalProperties": true,
            "description": "Every settings key this type stores, with its default value."
          }
        }
      },
      "LandingPageSectionUpdate": {
        "type": "object",
        "required": [
          "settings"
        ],
        "properties": {
          "settings": {
            "type": "object",
            "additionalProperties": true,
            "description": "Merged over the stored settings by default, so a single key can change without resending the rest. Lists replace wholesale. The same rules as on create apply: order-form toggles forced on, product ownership, list caps, the 262144-byte cap (a section already over it may be saved as long as it does not grow)."
          },
          "replace": {
            "type": "boolean",
            "default": false,
            "description": "When true, previous settings are dropped and the sent settings are merged over the type's defaults instead."
          }
        }
      },
      "LandingPageSummary": {
        "type": "object",
        "description": "A landing page as it appears in the list.",
        "required": [
          "id",
          "title",
          "slug",
          "public_url",
          "status",
          "language",
          "product_id",
          "views",
          "is_purchased",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "description": "URL segment, unique within the store."
          },
          "public_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Live address of the page (the store's address followed by /landing/ and the slug). Null when the store has no address yet. Hand this value to people as is; do not build the address yourself."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "draft"
            ]
          },
          "language": {
            "type": "string",
            "description": "Page language: ar, fr or en."
          },
          "product_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Linked product, or null."
          },
          "views": {
            "type": "integer",
            "description": "Read-only view counter."
          },
          "is_purchased": {
            "type": "boolean",
            "description": "True when the page was bought outright. On the Free plan this is what makes it visible."
          },
          "created_at": {
            "type": "string",
            "description": "Date and time in the store's database clock, formatted YYYY-MM-DD HH:MM:SS."
          },
          "updated_at": {
            "type": "string",
            "description": "Date and time in the store's database clock, formatted YYYY-MM-DD HH:MM:SS."
          }
        }
      },
      "LandingPageUpdate": {
        "type": "object",
        "description": "Send only the fields to change. An empty object changes nothing and returns the page.",
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Renaming also regenerates the slug unless slug is sent in the same request."
          },
          "slug": {
            "type": "string",
            "description": "Normalised (lower case, runs of other characters become a hyphen, at most 200 characters) and made unique within the store. The old address keeps working after a slug change."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "draft"
            ],
            "description": "Setting active is refused with page_not_publishable when the page has a blocking problem."
          },
          "language": {
            "type": "string",
            "enum": [
              "ar",
              "fr",
              "en"
            ]
          },
          "product_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "A product of this store, or null to unlink."
          },
          "meta_title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 255 characters. Empty string or null clears it."
          },
          "meta_description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Empty string or null clears it."
          }
        }
      },
      "Meta": {
        "type": "object",
        "description": "Metadata attached to every response.",
        "required": [
          "request_id",
          "api_version"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "description": "The X-Request-Id you sent, or an id generated for this request. Quote it when you contact support. On a cached GET (X-Cache: HIT) the body keeps the id of the request that filled the cache; the X-Request-Id header carries yours."
          },
          "api_version": {
            "type": "string",
            "enum": [
              "v1"
            ]
          },
          "edge": {
            "type": "boolean",
            "description": "Present and true when the edge answered without reaching the origin."
          }
        }
      },
      "Order": {
        "type": "object",
        "description": "Full order detail.",
        "required": [
          "id",
          "order_number",
          "store_seq",
          "status",
          "payment_status",
          "payment_method",
          "customer",
          "delivery",
          "shipment",
          "amounts",
          "items",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "order_number": {
            "type": "string"
          },
          "store_seq": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The per-store number the merchant sees in the dashboard (#644). Can be null for a few seconds after an API create."
          },
          "status": {
            "type": "string",
            "description": "One of the seven order statuses (see OrderStatus)."
          },
          "payment_status": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer": {
            "$ref": "#/components/schemas/OrderCustomer"
          },
          "delivery": {
            "$ref": "#/components/schemas/OrderDelivery"
          },
          "shipment": {
            "$ref": "#/components/schemas/OrderShipment"
          },
          "amounts": {
            "$ref": "#/components/schemas/OrderAmounts"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderItem"
            }
          },
          "created_at": {
            "type": "string",
            "description": "Date and time as `YYYY-MM-DD HH:MM:SS`, store server local time."
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Date and time as `YYYY-MM-DD HH:MM:SS`, store server local time."
          }
        }
      },
      "OrderAmounts": {
        "type": "object",
        "description": "Money in DZD.",
        "required": [
          "subtotal",
          "shipping_cost",
          "discount",
          "payment_fee",
          "total"
        ],
        "properties": {
          "subtotal": {
            "type": "number"
          },
          "shipping_cost": {
            "type": "number"
          },
          "discount": {
            "type": "number"
          },
          "payment_fee": {
            "type": "number"
          },
          "total": {
            "type": "number"
          }
        }
      },
      "OrderCustomer": {
        "type": "object",
        "required": [
          "id",
          "name",
          "phone",
          "email",
          "wilaya_id",
          "commune",
          "address"
        ],
        "properties": {
          "id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Customer id; see GET /v1/customers/{id}."
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "wilaya_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "commune": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "OrderDelivery": {
        "type": "object",
        "required": [
          "type",
          "desk_id",
          "desk_name"
        ],
        "properties": {
          "type": {
            "type": [
              "string",
              "null"
            ],
            "description": "home, desk, pickup or digital for API orders."
          },
          "desk_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "desk_name": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "OrderInput": {
        "type": "object",
        "required": [
          "customer",
          "items"
        ],
        "description": "Line prices come from the catalogue and shipping from the store's own rates; a price, shipping_cost or payment_fee in the body is ignored.",
        "properties": {
          "customer": {
            "$ref": "#/components/schemas/OrderInputCustomer"
          },
          "delivery": {
            "$ref": "#/components/schemas/OrderInputDelivery"
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/OrderInputItem"
            }
          },
          "discount": {
            "type": "number",
            "minimum": 0,
            "default": 0,
            "description": "DZD. Capped at subtotal plus shipping."
          },
          "payment_method": {
            "type": "string",
            "enum": [
              "cod",
              "free_digital",
              "digital_payment"
            ],
            "description": "Defaults to free_digital for digital delivery, cod otherwise."
          },
          "notes": {
            "type": "string",
            "description": "Customer notes shown on the dashboard order page. Cut to 1000 characters. Not returned by the API."
          }
        }
      },
      "OrderInputCustomer": {
        "type": "object",
        "required": [
          "name",
          "phone",
          "wilaya_id",
          "commune"
        ],
        "description": "The buyer. Matched to an existing customer of the store by exact phone; a match is updated (name, wilaya, commune, address, and email when sent), otherwise a customer is created.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Full name. Trimmed. Stored whole as the customer's first name."
          },
          "phone": {
            "type": "string",
            "pattern": "^\\+?[0-9 ]{6,20}$",
            "description": "Digits and spaces, optional leading +."
          },
          "email": {
            "type": "string",
            "description": "Saved on the order and the customer when present."
          },
          "wilaya_id": {
            "type": "integer",
            "minimum": 1,
            "maximum": 69,
            "description": "1-58, or 1-69 when the store uses the 69-wilaya list."
          },
          "commune": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "address": {
            "type": "string",
            "description": "Street and apartment. May be empty for desk delivery."
          }
        }
      },
      "OrderInputDelivery": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "home",
              "desk",
              "pickup",
              "digital"
            ],
            "default": "home",
            "description": "When the store has this type turned off for the wilaya, the order switches to the type the store offers, the same way a dashboard order does. pickup and digital never carry a shipping charge."
          },
          "desk_id": {
            "type": "integer",
            "description": "Pickup desk id for desk delivery."
          },
          "desk_name": {
            "type": "string",
            "description": "Human-readable desk label."
          }
        }
      },
      "OrderInputItem": {
        "type": "object",
        "required": [
          "product_id"
        ],
        "properties": {
          "product_id": {
            "type": "integer",
            "minimum": 0,
            "description": "Must belong to the store."
          },
          "quantity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 9999,
            "default": 1
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderInputVariant"
            },
            "description": "One entry per chosen group."
          }
        }
      },
      "OrderInputVariant": {
        "type": "object",
        "properties": {
          "group_name": {
            "type": "string",
            "description": "Variant group name as GET /v1/products/{id} returns it. Stored cut to 100 characters."
          },
          "option_name": {
            "type": "string",
            "description": "Option name as GET /v1/products/{id} returns it. Stored cut to 100 characters."
          },
          "color_code": {
            "type": "string",
            "description": "Hex colour. Stored cut to 7 characters."
          },
          "price_adjustment": {
            "type": "number",
            "default": 0,
            "description": "Replaced by the catalogue value whenever the group and option exist on the product; kept only for pairs the catalogue does not have."
          }
        }
      },
      "OrderItem": {
        "type": "object",
        "required": [
          "id",
          "product_id",
          "price",
          "quantity",
          "variants"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "product_id": {
            "type": "integer"
          },
          "price": {
            "type": "number",
            "description": "Unit price in DZD including variant adjustments."
          },
          "quantity": {
            "type": "integer"
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderItemVariant"
            }
          }
        }
      },
      "OrderItemVariant": {
        "type": "object",
        "description": "One chosen option for one variant group on a line.",
        "required": [
          "order_item_id",
          "group_name",
          "option_name",
          "color_code",
          "price_adjustment"
        ],
        "properties": {
          "order_item_id": {
            "type": "integer"
          },
          "group_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "option_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "color_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hex colour for colour groups."
          },
          "price_adjustment": {
            "type": [
              "string",
              "null"
            ],
            "description": "Per-piece price added by this option, returned as a decimal string such as \"200.00\"."
          }
        }
      },
      "OrderList": {
        "type": "object",
        "required": [
          "items",
          "next_cursor",
          "has_more"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderSummary"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "OrderSendFailure": {
        "type": "object",
        "description": "Most recent refused courier send for this order.",
        "required": [
          "at",
          "provider",
          "message"
        ],
        "properties": {
          "at": {
            "type": "string",
            "description": "Date and time as `YYYY-MM-DD HH:MM:SS`, store server local time."
          },
          "provider": {
            "type": [
              "string",
              "null"
            ],
            "description": "Courier slug."
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "description": "The courier's own refusal message."
          }
        }
      },
      "OrderSendQueued": {
        "type": "object",
        "required": [
          "order_id",
          "queued",
          "workflow_id",
          "note"
        ],
        "properties": {
          "order_id": {
            "type": "integer"
          },
          "queued": {
            "type": "boolean",
            "const": true
          },
          "workflow_id": {
            "type": "string",
            "description": "Identifier of the background send."
          },
          "note": {
            "type": "string",
            "description": "Plain-text polling hint."
          }
        }
      },
      "OrderSendResult": {
        "type": "object",
        "required": [
          "order_id",
          "sent",
          "provider",
          "tracking",
          "message"
        ],
        "properties": {
          "order_id": {
            "type": "integer"
          },
          "sent": {
            "type": "boolean",
            "const": true
          },
          "provider": {
            "type": [
              "string",
              "null"
            ],
            "description": "Courier slug that took the parcel."
          },
          "tracking": {
            "type": [
              "string",
              "null"
            ]
          },
          "message": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "OrderSendToDeliveryInput": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "description": "Courier slug. Empty or absent uses the store's default courier."
          },
          "confirm_token": {
            "type": "string",
            "description": "Token from a previous confirmation_required answer, sent once the merchant approved the summary."
          }
        }
      },
      "OrderShipment": {
        "type": "object",
        "required": [
          "sent_to_delivery",
          "sent_to_delivery_at",
          "delivery_company",
          "delivery_tracking",
          "last_send_failure"
        ],
        "properties": {
          "sent_to_delivery": {
            "type": "boolean",
            "description": "True once a courier accepted the parcel."
          },
          "sent_to_delivery_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Date and time as `YYYY-MM-DD HH:MM:SS`, store server local time."
          },
          "delivery_company": {
            "type": [
              "string",
              "null"
            ],
            "description": "Courier slug."
          },
          "delivery_tracking": {
            "type": [
              "string",
              "null"
            ],
            "description": "Courier tracking number."
          },
          "last_send_failure": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/OrderSendFailure"
              },
              {
                "type": "null"
              }
            ],
            "description": "Latest refused send. Always null once sent_to_delivery is true."
          }
        }
      },
      "OrderStatus": {
        "type": "string",
        "enum": [
          "pending",
          "confirmed",
          "processing",
          "shipped",
          "delivered",
          "cancelled",
          "returned"
        ],
        "description": "Fulfilment status. Allowed moves: pending to confirmed, processing or cancelled; confirmed to processing, shipped or cancelled; processing to shipped or cancelled; shipped to delivered or returned; delivered to returned. cancelled and returned are terminal."
      },
      "OrderSummary": {
        "type": "object",
        "description": "Compact order row used by the order list. No items, no variants.",
        "required": [
          "id",
          "order_number",
          "status",
          "payment_status",
          "payment_method",
          "total",
          "customer_name",
          "customer_phone",
          "wilaya_id",
          "commune",
          "delivery_type",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "order_number": {
            "type": "string",
            "description": "ORD-{store_id}-{YYYYMMDD}-{8 hex} for API and dashboard orders; landing-page orders use LP-{8 hex}; older orders carry a 4-hex suffix."
          },
          "status": {
            "type": "string",
            "description": "One of the seven order statuses (see OrderStatus)."
          },
          "payment_status": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "total": {
            "type": "number",
            "description": "Order total in DZD."
          },
          "customer_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "wilaya_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "commune": {
            "type": [
              "string",
              "null"
            ]
          },
          "delivery_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "description": "Date and time as `YYYY-MM-DD HH:MM:SS`, store server local time."
          }
        }
      },
      "OrderUpdate": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "$ref": "#/components/schemas/OrderStatus"
          }
        }
      },
      "Ping": {
        "type": "object",
        "required": [
          "pong",
          "time"
        ],
        "properties": {
          "pong": {
            "type": "boolean",
            "const": true
          },
          "time": {
            "type": "string",
            "description": "ISO 8601 time with offset from the origin, or UTC with milliseconds from the edge."
          },
          "edge": {
            "type": "boolean",
            "description": "Present and true when the edge answered."
          }
        }
      },
      "Pixel": {
        "type": "object",
        "description": "A tracking pixel. The server-side access token is never returned; `has_token` says whether one is stored.",
        "required": [
          "id",
          "pixel_type",
          "pixel_id",
          "pixel_name",
          "has_token",
          "test_event_code",
          "ad_account_id",
          "conversion_label",
          "is_active",
          "is_default",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "description": "DZBuild id of this pixel entry."
          },
          "pixel_type": {
            "type": "string",
            "enum": [
              "facebook",
              "tiktok",
              "snapchat",
              "pinterest",
              "google_analytics",
              "gtm",
              "google_ads"
            ]
          },
          "pixel_id": {
            "type": "string",
            "description": "The pixel or measurement id from the ad platform."
          },
          "pixel_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_token": {
            "type": "boolean",
            "description": "True when a server-side (Conversions API / Events API) access token is stored."
          },
          "test_event_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Read-only here; it cannot be set through the API."
          },
          "ad_account_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "conversion_label": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_active": {
            "type": "boolean"
          },
          "is_default": {
            "type": "boolean"
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Date-time in the `YYYY-MM-DD HH:MM:SS` form, Africa/Algiers local time."
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Date-time in the `YYYY-MM-DD HH:MM:SS` form, Africa/Algiers local time."
          }
        }
      },
      "PixelInput": {
        "type": "object",
        "required": [
          "pixel_type",
          "pixel_id"
        ],
        "properties": {
          "pixel_type": {
            "type": "string",
            "enum": [
              "facebook",
              "tiktok",
              "snapchat",
              "pinterest",
              "google_analytics",
              "gtm",
              "google_ads"
            ],
            "description": "Case-insensitive. `type` is accepted as an alias. Cannot be changed later."
          },
          "pixel_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[A-Za-z0-9_-]+$",
            "description": "Letters, digits, hyphen and underscore. Unique per type within the store. Cannot be changed later."
          },
          "pixel_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Display name, cut to 100 characters. `name` is accepted as an alias."
          },
          "ad_account_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 64 characters."
          },
          "conversion_label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 64 characters."
          },
          "is_active": {
            "type": "boolean",
            "default": true
          },
          "is_default": {
            "type": "boolean",
            "description": "Setting true clears is_default on the store's other pixels of the same type.",
            "default": false
          },
          "access_token": {
            "type": "string",
            "description": "Server-side access token copied from the ad platform's events manager. Quotes and invisible characters are stripped; it must not contain `<` or whitespace and must not equal pixel_id. Facebook tokens are at least 40 characters. Never returned. Omit or send an empty string for no token."
          }
        }
      },
      "PixelLimits": {
        "type": "object",
        "description": "Plan allowance and current usage.",
        "required": [
          "plan",
          "can_add",
          "per_type_limit",
          "total_limit",
          "counts",
          "total"
        ],
        "properties": {
          "plan": {
            "type": "string",
            "description": "The store plan the limits come from."
          },
          "can_add": {
            "type": "boolean",
            "description": "Whether the plan allows pixels at all. It does not account for per-type counts already used."
          },
          "per_type_limit": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Pixels allowed per type; null means unlimited."
          },
          "total_limit": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Pixels allowed in total; null means unlimited."
          },
          "counts": {
            "type": "object",
            "description": "Pixels per type, one key for every pixel type.",
            "properties": {
              "facebook": {
                "type": "integer"
              },
              "tiktok": {
                "type": "integer"
              },
              "snapchat": {
                "type": "integer"
              },
              "pinterest": {
                "type": "integer"
              },
              "google_analytics": {
                "type": "integer"
              },
              "gtm": {
                "type": "integer"
              },
              "google_ads": {
                "type": "integer"
              }
            },
            "additionalProperties": {
              "type": "integer"
            }
          },
          "total": {
            "type": "integer"
          }
        }
      },
      "PixelUpdate": {
        "type": "object",
        "description": "Partial update: only the keys you send are changed. Sending pixel_type, pixel_id or type is rejected.",
        "properties": {
          "pixel_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Display name, cut to 100 characters."
          },
          "ad_account_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 64 characters."
          },
          "conversion_label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 64 characters."
          },
          "is_active": {
            "type": "boolean"
          },
          "is_default": {
            "type": "boolean",
            "description": "Setting true clears is_default on the store's other pixels of the same type."
          },
          "access_token": {
            "type": "string",
            "description": "Server-side access token copied from the ad platform's events manager. Quotes and invisible characters are stripped; it must not contain `<` or whitespace and must not equal pixel_id. Facebook tokens are at least 40 characters. Never returned. An empty or masked value keeps the stored token; a token cannot be cleared through the API, only replaced."
          }
        }
      },
      "Product": {
        "type": "object",
        "description": "Full product detail.",
        "required": [
          "id",
          "name",
          "slug",
          "description",
          "short_description",
          "category_id",
          "pricing",
          "inventory",
          "shipping",
          "status",
          "featured",
          "has_variants",
          "images",
          "variants",
          "combinations",
          "combination_count",
          "combinations_truncated",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Sanitized HTML description."
          },
          "short_description": {
            "type": [
              "string",
              "null"
            ]
          },
          "category_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Primary category id."
          },
          "pricing": {
            "type": "object",
            "required": [
              "price",
              "compare_price",
              "cost_price"
            ],
            "properties": {
              "price": {
                "type": "number",
                "description": "DZD."
              },
              "compare_price": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "DZD."
              },
              "cost_price": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "DZD. Never shown to buyers."
              }
            }
          },
          "inventory": {
            "type": "object",
            "required": [
              "sku",
              "barcode",
              "track_stock",
              "stock_quantity",
              "low_stock_alert"
            ],
            "properties": {
              "sku": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "barcode": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "track_stock": {
                "type": "boolean"
              },
              "stock_quantity": {
                "type": "integer"
              },
              "low_stock_alert": {
                "type": "integer"
              }
            }
          },
          "shipping": {
            "type": "object",
            "required": [
              "weight",
              "height",
              "width",
              "length",
              "do_insurance"
            ],
            "properties": {
              "weight": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "height": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "width": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "length": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "do_insurance": {
                "type": "boolean"
              }
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "draft",
              "archived"
            ]
          },
          "featured": {
            "type": "boolean"
          },
          "has_variants": {
            "type": "boolean"
          },
          "images": {
            "type": "array",
            "description": "Primary image first, then by sort_order.",
            "items": {
              "$ref": "#/components/schemas/ProductImage"
            }
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductVariantGroup"
            }
          },
          "combinations": {
            "type": "array",
            "description": "At most 300 entries.",
            "items": {
              "$ref": "#/components/schemas/ProductVariantCombination"
            }
          },
          "combination_count": {
            "type": "integer"
          },
          "combinations_truncated": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "description": "Timestamp in the form YYYY-MM-DD HH:MM:SS."
          },
          "updated_at": {
            "type": "string",
            "description": "Timestamp in the form YYYY-MM-DD HH:MM:SS."
          }
        }
      },
      "ProductAddon": {
        "type": "object",
        "required": [
          "id",
          "title",
          "input_type",
          "placeholder",
          "is_required",
          "extra_price",
          "max_length",
          "allowed_extensions",
          "sort_order",
          "is_active"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "input_type": {
            "type": "string",
            "enum": [
              "text",
              "textarea",
              "image"
            ]
          },
          "placeholder": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_required": {
            "type": "boolean"
          },
          "extra_price": {
            "type": "number",
            "description": "Extra charge in DZD."
          },
          "max_length": {
            "type": [
              "integer",
              "null"
            ]
          },
          "allowed_extensions": {
            "type": [
              "string",
              "null"
            ],
            "description": "Comma-separated list, image fields only."
          },
          "sort_order": {
            "type": "integer"
          },
          "is_active": {
            "type": "boolean"
          }
        }
      },
      "ProductAddons": {
        "type": "object",
        "required": [
          "product_id",
          "enabled",
          "addons"
        ],
        "properties": {
          "product_id": {
            "type": "integer"
          },
          "enabled": {
            "type": "boolean",
            "description": "Master switch for the product's input fields."
          },
          "addons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductAddon"
            }
          }
        }
      },
      "ProductAddonsInput": {
        "type": "object",
        "required": [
          "addons"
        ],
        "properties": {
          "enabled": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Sets the master switch; omitted or null leaves it unchanged."
          },
          "addons": {
            "type": "array",
            "maxItems": 20,
            "description": "Full replace in display order; [] removes all fields.",
            "items": {
              "type": "object",
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string",
                  "description": "Cut to 255 characters.",
                  "minLength": 1
                },
                "input_type": {
                  "type": "string",
                  "description": "Default text.",
                  "enum": [
                    "text",
                    "textarea",
                    "image"
                  ]
                },
                "placeholder": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Cut to 255 characters."
                },
                "is_required": {
                  "type": "boolean"
                },
                "extra_price": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "DZD, 0 or more. Default 0.",
                  "minimum": 0
                },
                "max_length": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Positive integer, capped at 65535. Not allowed on image fields.",
                  "minimum": 1
                },
                "allowed_extensions": {
                  "description": "Image fields only: jpg, jpeg, png, gif, webp, as a list or a comma-separated string. Defaults to all five.",
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "jpg",
                          "jpeg",
                          "png",
                          "gif",
                          "webp"
                        ]
                      }
                    },
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "is_active": {
                  "type": "boolean",
                  "description": "Default true."
                }
              }
            }
          }
        }
      },
      "ProductDeleted": {
        "type": "object",
        "required": [
          "deleted",
          "id"
        ],
        "properties": {
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "id": {
            "type": "integer"
          }
        }
      },
      "ProductImage": {
        "type": "object",
        "description": "A product image. `file_size` appears on the image write endpoints; `width` and `height` appear only on a freshly added image (not on a deduplicated one).",
        "required": [
          "id",
          "url",
          "alt_text",
          "is_primary",
          "sort_order"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "url": {
            "type": "string",
            "description": "Full CDN URL."
          },
          "alt_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_primary": {
            "type": "boolean"
          },
          "sort_order": {
            "type": "integer"
          },
          "file_size": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Stored file size in bytes."
          },
          "width": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Stored width in pixels."
          },
          "height": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Stored height in pixels."
          }
        }
      },
      "ProductImageAddResult": {
        "type": "object",
        "required": [
          "image",
          "deduplicated"
        ],
        "properties": {
          "image": {
            "$ref": "#/components/schemas/ProductImage"
          },
          "deduplicated": {
            "type": "boolean",
            "description": "True when the same bytes were already attached; the existing image is returned and nothing is added."
          }
        }
      },
      "ProductImageDeleted": {
        "type": "object",
        "required": [
          "deleted",
          "new_primary_image_id",
          "variant_references_cleared",
          "remaining_images"
        ],
        "properties": {
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "new_primary_image_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The product's primary image after the delete, null when no image is left."
          },
          "variant_references_cleared": {
            "type": "integer",
            "description": "Variant options that pointed at this image and lost that link."
          },
          "remaining_images": {
            "type": "integer"
          }
        }
      },
      "ProductImageInput": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "description": "Public https URL of the image file, at most 2000 characters.",
            "maxLength": 2000
          },
          "alt_text": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 255 characters."
          },
          "is_primary": {
            "type": "boolean",
            "description": "Make this the primary image. The first image of a product is always primary."
          }
        }
      },
      "ProductImageUpdate": {
        "type": "object",
        "description": "Any subset of the fields below.",
        "properties": {
          "alt_text": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 255 characters; empty or null clears it."
          },
          "is_primary": {
            "type": "boolean",
            "description": "Only true is accepted; false answers 422 primary_required."
          },
          "sort_order": {
            "type": "integer",
            "minimum": 0,
            "maximum": 999
          }
        }
      },
      "ProductImageUpdateResult": {
        "type": "object",
        "required": [
          "image"
        ],
        "properties": {
          "image": {
            "$ref": "#/components/schemas/ProductImage"
          }
        }
      },
      "ProductInput": {
        "type": "object",
        "description": "Create body. The slug is always derived from name; a slug in this body is ignored.",
        "required": [
          "name",
          "price"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "1 to 255 bytes of UTF-8 after trimming (about 127 Arabic letters).",
            "minLength": 1
          },
          "price": {
            "type": "number",
            "description": "DZD, 0 or more.",
            "minimum": 0
          },
          "compare_price": {
            "type": [
              "number",
              "null"
            ],
            "description": "Strike-through price in DZD, or null. Create does not reject negative values."
          },
          "cost_price": {
            "type": [
              "number",
              "null"
            ],
            "description": "DZD, or null. Never shown to buyers. Create does not reject negative values."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "HTML description. Sanitized on save; rejected when the cleaned HTML exceeds 60000 bytes."
          },
          "short_description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 500 characters."
          },
          "sku": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 100 characters."
          },
          "barcode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 100 characters."
          },
          "weight": {
            "type": [
              "number",
              "null"
            ],
            "description": "Must be numeric or null; create does not reject negative values."
          },
          "shipping_height": {
            "type": [
              "number",
              "null"
            ],
            "description": "Must be numeric or null; create does not reject negative values."
          },
          "shipping_width": {
            "type": [
              "number",
              "null"
            ],
            "description": "Must be numeric or null; create does not reject negative values."
          },
          "shipping_length": {
            "type": [
              "number",
              "null"
            ],
            "description": "Must be numeric or null; create does not reject negative values."
          },
          "do_insurance": {
            "type": "boolean"
          },
          "track_stock": {
            "type": "boolean",
            "description": "Forced off when variant_stock_enabled or combination_stock_enabled is on."
          },
          "stock_quantity": {
            "type": "integer",
            "description": "Default 0. Negative values are stored as 0."
          },
          "low_stock_alert": {
            "type": "integer",
            "description": "Default 5. Negative values are stored as 0."
          },
          "variant_stock_enabled": {
            "type": "boolean",
            "description": "Track stock per variant option."
          },
          "combination_stock_enabled": {
            "type": "boolean",
            "description": "Track stock per variant combination. Turns variant_stock_enabled on."
          },
          "category_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Must be a category of this store."
          },
          "featured": {
            "type": "boolean",
            "description": "Default false."
          },
          "status": {
            "type": "string",
            "description": "Default draft. Any other value is stored as draft.",
            "enum": [
              "active",
              "draft",
              "archived"
            ]
          }
        }
      },
      "ProductList": {
        "type": "object",
        "required": [
          "items",
          "next_cursor",
          "has_more"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductSummary"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as cursor to fetch the next page; null on the last page."
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "ProductOffer": {
        "type": "object",
        "required": [
          "id",
          "title",
          "quantity",
          "price",
          "compare_price",
          "discount_type",
          "discount_value",
          "badge_text",
          "badge_color",
          "free_shipping",
          "image_path",
          "image_url",
          "sort_order",
          "is_active"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "quantity": {
            "type": "integer",
            "description": "Units in the bundle."
          },
          "price": {
            "type": "number",
            "description": "TOTAL price in DZD for the whole bundle, not a unit price. 0 when the offer uses discount_type."
          },
          "compare_price": {
            "type": [
              "number",
              "null"
            ],
            "description": "DZD."
          },
          "discount_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "null for a fixed-price offer.",
            "enum": [
              "amount",
              "percent",
              null
            ]
          },
          "discount_value": {
            "type": [
              "number",
              "null"
            ],
            "description": "Amount in DZD or a percentage, taken off product price times quantity."
          },
          "badge_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "badge_color": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hex colour."
          },
          "free_shipping": {
            "type": "boolean"
          },
          "image_path": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque reference to the offer picture; echo it back on write to keep the picture."
          },
          "image_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Full CDN URL of the offer picture."
          },
          "sort_order": {
            "type": "integer"
          },
          "is_active": {
            "type": "boolean"
          }
        }
      },
      "ProductOffers": {
        "type": "object",
        "required": [
          "product_id",
          "pricing_note",
          "offers"
        ],
        "properties": {
          "product_id": {
            "type": "integer"
          },
          "pricing_note": {
            "type": "string",
            "description": "Fixed reminder that price is the bundle total."
          },
          "offers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductOffer"
            }
          }
        }
      },
      "ProductOffersInput": {
        "type": "object",
        "required": [
          "offers"
        ],
        "properties": {
          "offers": {
            "type": "array",
            "maxItems": 50,
            "description": "Full replace in display order; [] removes all offers. Every offer needs a distinct quantity.",
            "items": {
              "type": "object",
              "required": [
                "title",
                "quantity"
              ],
              "properties": {
                "title": {
                  "type": "string",
                  "description": "Cut to 255 characters.",
                  "minLength": 1
                },
                "quantity": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 9999
                },
                "price": {
                  "type": "number",
                  "description": "TOTAL for the bundle in DZD, greater than 0. Required unless discount_type is set; ignored (stored as 0) when it is."
                },
                "discount_type": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "enum": [
                    "amount",
                    "percent",
                    null
                  ]
                },
                "discount_value": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Required with discount_type: greater than 0, and at most 100 for percent."
                },
                "compare_price": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "DZD, 0 or more.",
                  "minimum": 0
                },
                "badge_text": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Cut to 100 characters."
                },
                "badge_color": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Hex colour #rgb or #rrggbb. Default #10b981."
                },
                "free_shipping": {
                  "type": "boolean"
                },
                "image_path": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Must be an image_path this product's offers already use, as returned by GET. New pictures are uploaded in the dashboard."
                },
                "is_active": {
                  "type": "boolean",
                  "description": "Default true."
                }
              }
            }
          }
        }
      },
      "ProductQuantityRules": {
        "type": "object",
        "required": [
          "product_id",
          "min_qty",
          "max_qty",
          "has_rule",
          "addon_id",
          "addon_active"
        ],
        "properties": {
          "product_id": {
            "type": "integer"
          },
          "min_qty": {
            "type": "integer",
            "description": "0 means no minimum."
          },
          "max_qty": {
            "type": "integer",
            "description": "0 means no maximum."
          },
          "has_rule": {
            "type": "boolean"
          },
          "addon_id": {
            "type": "string",
            "enum": [
              "min-max-quantity"
            ]
          },
          "addon_active": {
            "type": "boolean",
            "description": "Whether the Minimum & Maximum Quantity Per Product add-on is active for the store. The rule is only enforced at checkout when it is."
          },
          "warning": {
            "type": "string",
            "description": "Present when a rule is saved but the add-on is not active."
          }
        }
      },
      "ProductQuantityRulesInput": {
        "type": "object",
        "description": "Both 0 (or both omitted) removes the rule.",
        "properties": {
          "min_qty": {
            "type": "integer",
            "description": "0 to 10000, default 0 (no limit).",
            "minimum": 0,
            "maximum": 10000
          },
          "max_qty": {
            "type": "integer",
            "description": "0 to 10000, default 0 (no limit). When above 0 it must be at least min_qty.",
            "minimum": 0,
            "maximum": 10000
          }
        }
      },
      "ProductStock": {
        "type": "object",
        "description": "Current stock mode and every target that can be set. `product` appears in product mode; `options` in variant_options mode and, read-only, in combinations mode; `combinations` in combinations mode.",
        "required": [
          "product_id",
          "mode",
          "track_stock",
          "flags",
          "max_stock"
        ],
        "properties": {
          "product_id": {
            "type": "integer"
          },
          "mode": {
            "type": "string",
            "enum": [
              "product",
              "variant_options",
              "combinations"
            ]
          },
          "track_stock": {
            "type": "boolean"
          },
          "flags": {
            "type": "object",
            "required": [
              "variant_stock_enabled",
              "combination_stock_enabled"
            ],
            "properties": {
              "variant_stock_enabled": {
                "type": "boolean"
              },
              "combination_stock_enabled": {
                "type": "boolean"
              }
            }
          },
          "max_stock": {
            "type": "integer",
            "description": "Largest value a stock count can hold (9999999)."
          },
          "product": {
            "type": "object",
            "required": [
              "target",
              "stock_quantity"
            ],
            "properties": {
              "target": {
                "type": "string",
                "enum": [
                  "product"
                ]
              },
              "stock_quantity": {
                "type": "integer"
              }
            }
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "target",
                "id",
                "group",
                "value",
                "stock",
                "unlimited"
              ],
              "properties": {
                "target": {
                  "type": "string",
                  "enum": [
                    "option"
                  ]
                },
                "id": {
                  "type": "integer"
                },
                "group": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                },
                "stock": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "null means unlimited."
                },
                "unlimited": {
                  "type": "boolean"
                }
              }
            }
          },
          "combinations": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "target",
                "id",
                "sku",
                "stock",
                "is_active",
                "options"
              ],
              "properties": {
                "target": {
                  "type": "string",
                  "enum": [
                    "combination"
                  ]
                },
                "id": {
                  "type": "integer"
                },
                "sku": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "stock": {
                  "type": "integer"
                },
                "is_active": {
                  "type": "boolean"
                },
                "options": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      },
      "ProductStockInput": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "description": "Each item carries exactly one of set, delta or unlimited: true. A target may appear once per request.",
            "items": {
              "type": "object",
              "required": [
                "target"
              ],
              "properties": {
                "target": {
                  "type": "string",
                  "description": "Must match the product's stock mode: product, option (variant_options) or combination (combinations).",
                  "enum": [
                    "product",
                    "option",
                    "combination"
                  ]
                },
                "id": {
                  "type": "integer",
                  "description": "Option or combination id; required unless target is product.",
                  "minimum": 1
                },
                "set": {
                  "type": "integer",
                  "description": "Absolute value, 0 to 9999999. Setting product stock also turns track_stock on.",
                  "minimum": 0,
                  "maximum": 9999999
                },
                "delta": {
                  "type": "integer",
                  "description": "Relative change, not 0; the result is kept between 0 and 9999999."
                },
                "unlimited": {
                  "type": "boolean",
                  "description": "Options only. true makes the option unlimited. An option that is unlimited today needs unlimited: false alongside set to start tracking it."
                }
              }
            }
          }
        }
      },
      "ProductSummary": {
        "type": "object",
        "description": "One product in a list page.",
        "required": [
          "id",
          "name",
          "slug",
          "short_description",
          "price",
          "compare_price",
          "sku",
          "stock_quantity",
          "track_stock",
          "status",
          "has_variants",
          "featured",
          "primary_image",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "short_description": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "type": "number",
            "description": "Price in DZD."
          },
          "compare_price": {
            "type": [
              "number",
              "null"
            ],
            "description": "Strike-through price in DZD, or null."
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "stock_quantity": {
            "type": "integer",
            "description": "Product-level stock. Only meaningful when track_stock is true."
          },
          "track_stock": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "draft",
              "archived"
            ]
          },
          "has_variants": {
            "type": "boolean"
          },
          "featured": {
            "type": "boolean"
          },
          "primary_image": {
            "type": [
              "string",
              "null"
            ],
            "description": "Full CDN URL of the primary image (or the first image by display order), null when the product has no image."
          },
          "created_at": {
            "type": "string",
            "description": "Timestamp in the form YYYY-MM-DD HH:MM:SS."
          },
          "updated_at": {
            "type": "string",
            "description": "Timestamp in the form YYYY-MM-DD HH:MM:SS."
          }
        }
      },
      "ProductUpdate": {
        "type": "object",
        "description": "Partial update. Only the keys present are changed; an empty object is a successful no-op.",
        "properties": {
          "name": {
            "type": "string",
            "description": "1 to 255 bytes of UTF-8 after trimming (about 127 Arabic letters). Changing the name regenerates the slug unless slug is also sent.",
            "minLength": 1
          },
          "price": {
            "type": "number",
            "description": "DZD, 0 or more.",
            "minimum": 0
          },
          "compare_price": {
            "type": [
              "number",
              "null"
            ],
            "description": "DZD, 0 or more, or null.",
            "minimum": 0
          },
          "cost_price": {
            "type": [
              "number",
              "null"
            ],
            "description": "DZD, 0 or more, or null. Never shown to buyers.",
            "minimum": 0
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "HTML description. Sanitized on save; rejected when the cleaned HTML exceeds 60000 bytes."
          },
          "short_description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 500 characters."
          },
          "sku": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 255 characters."
          },
          "barcode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 255 characters."
          },
          "weight": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "shipping_height": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "shipping_width": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "shipping_length": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "do_insurance": {
            "type": "boolean"
          },
          "track_stock": {
            "type": "boolean",
            "description": "Forced off when variant_stock_enabled or combination_stock_enabled is on."
          },
          "stock_quantity": {
            "type": "integer",
            "description": "Negative values are stored as 0."
          },
          "low_stock_alert": {
            "type": "integer",
            "description": "Negative values are stored as 0."
          },
          "variant_stock_enabled": {
            "type": "boolean",
            "description": "Track stock per variant option."
          },
          "combination_stock_enabled": {
            "type": "boolean",
            "description": "Track stock per variant combination. Turns track_stock off."
          },
          "category_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Must be a category of this store. null clears the primary category."
          },
          "featured": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "description": "Any other value answers 400.",
            "enum": [
              "active",
              "draft",
              "archived"
            ]
          },
          "slug": {
            "type": "string",
            "description": "Lowercased; every run of characters other than letters and digits becomes a hyphen; cut to 200 characters. A clash gets a -2, -3 suffix."
          }
        }
      },
      "ProductVariantCombination": {
        "type": "object",
        "required": [
          "id",
          "sku",
          "stock",
          "is_active",
          "options"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "stock": {
            "type": "integer"
          },
          "is_active": {
            "type": "boolean"
          },
          "options": {
            "type": "object",
            "description": "Group name to option name, one entry per non-selectable group.",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "ProductVariantGroup": {
        "type": "object",
        "required": [
          "id",
          "name",
          "type",
          "required",
          "sort_order",
          "options"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "description": "Display type of the group.",
            "enum": [
              "text",
              "color",
              "image_text",
              "selectable",
              "dropdown"
            ]
          },
          "required": {
            "type": "boolean"
          },
          "sort_order": {
            "type": "integer"
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductVariantOption"
            }
          }
        }
      },
      "ProductVariantOption": {
        "type": "object",
        "required": [
          "id",
          "value",
          "color_code",
          "price_adjustment",
          "stock",
          "sku",
          "image_id",
          "show_as_card",
          "sort_order",
          "is_active"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "value": {
            "type": "string",
            "description": "Option name as shown to buyers."
          },
          "color_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hex colour #rrggbb for colour groups."
          },
          "price_adjustment": {
            "type": "number",
            "description": "Added to the base price in DZD, may be negative."
          },
          "stock": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Per-option stock; null means unlimited."
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "image_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Id of one of this product's images."
          },
          "show_as_card": {
            "type": "boolean"
          },
          "sort_order": {
            "type": "integer"
          },
          "is_active": {
            "type": "boolean"
          }
        }
      },
      "ProductVariants": {
        "type": "object",
        "description": "Variant groups plus stock combinations. `combinations` lists at most 300 entries; `combination_count` is always the true total.",
        "required": [
          "variants",
          "combinations",
          "combination_count",
          "combinations_truncated"
        ],
        "properties": {
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductVariantGroup"
            }
          },
          "combinations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductVariantCombination"
            }
          },
          "combination_count": {
            "type": "integer"
          },
          "combinations_truncated": {
            "type": "boolean"
          }
        }
      },
      "ProductVariantsInput": {
        "type": "object",
        "description": "Full replace. Everything is validated before anything is deleted.",
        "required": [
          "groups"
        ],
        "properties": {
          "groups": {
            "type": "array",
            "maxItems": 10,
            "description": "Variant groups in display order; [] removes all variants. At most 200 options across all groups.",
            "items": {
              "type": "object",
              "required": [
                "name",
                "options"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "description": "1 to 100 characters, unique per product (case-insensitive).",
                  "minLength": 1,
                  "maxLength": 100
                },
                "type": {
                  "type": "string",
                  "description": "Default text. selectable is an optional multi-select group and is never required.",
                  "enum": [
                    "text",
                    "color",
                    "image_text",
                    "selectable",
                    "dropdown"
                  ]
                },
                "required": {
                  "type": "boolean",
                  "description": "Default true."
                },
                "options": {
                  "type": "array",
                  "minItems": 1,
                  "maxItems": 100,
                  "items": {
                    "type": "object",
                    "required": [
                      "name"
                    ],
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "1 to 100 characters, unique inside the group (case-insensitive).",
                        "minLength": 1,
                        "maxLength": 100
                      },
                      "color_code": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "#rrggbb.",
                        "pattern": "^#[0-9a-fA-F]{6}$"
                      },
                      "price_adjustment": {
                        "type": [
                          "number",
                          "null"
                        ],
                        "description": "DZD added to the base price, may be negative. Default 0."
                      },
                      "stock": {
                        "type": [
                          "integer",
                          "null"
                        ],
                        "description": "Per-option stock, 0 or more; null means unlimited.",
                        "minimum": 0
                      },
                      "sku": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Cut to 100 characters."
                      },
                      "image_id": {
                        "type": [
                          "integer",
                          "null"
                        ],
                        "description": "Must be an image of this product."
                      },
                      "show_as_card": {
                        "type": "boolean"
                      }
                    }
                  }
                }
              }
            }
          },
          "combinations": {
            "type": "array",
            "maxItems": 1000,
            "description": "Per-combination stock. Needs at least 2 non-selectable groups.",
            "items": {
              "type": "object",
              "required": [
                "options",
                "stock"
              ],
              "properties": {
                "options": {
                  "type": "object",
                  "description": "Group name to option name, one entry for every non-selectable group (matched case-insensitively).",
                  "additionalProperties": {
                    "type": "string"
                  }
                },
                "stock": {
                  "type": "integer",
                  "minimum": 0
                },
                "sku": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Cut to 100 characters."
                },
                "is_active": {
                  "type": "boolean",
                  "description": "Default true."
                }
              }
            }
          }
        }
      },
      "PromoCode": {
        "type": "object",
        "description": "A discount code. The discount always applies to the whole order subtotal.",
        "required": [
          "id",
          "code",
          "discount_type",
          "discount_value",
          "min_order_amount",
          "max_uses",
          "used_count",
          "is_active",
          "starts_at",
          "expires_at",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "code": {
            "type": "string",
            "description": "Upper case, A-Z, 0-9, hyphen and underscore."
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "percentage",
              "fixed"
            ]
          },
          "discount_value": {
            "type": "number",
            "description": "Percent (0 to 100) for `percentage`, DZD amount for `fixed`."
          },
          "min_order_amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Minimum order subtotal in DZD, or null for no minimum."
          },
          "max_uses": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Usage ceiling, or null for unlimited."
          },
          "used_count": {
            "type": "integer",
            "description": "Times the code has been used. Read-only."
          },
          "is_active": {
            "type": "boolean"
          },
          "starts_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Date-time in the `YYYY-MM-DD HH:MM:SS` form, Africa/Algiers local time."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Date-time in the `YYYY-MM-DD HH:MM:SS` form, Africa/Algiers local time."
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Date-time in the `YYYY-MM-DD HH:MM:SS` form, Africa/Algiers local time."
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Date-time in the `YYYY-MM-DD HH:MM:SS` form, Africa/Algiers local time."
          }
        }
      },
      "PromoCodeInput": {
        "type": "object",
        "required": [
          "code",
          "discount_type",
          "discount_value"
        ],
        "properties": {
          "code": {
            "type": "string",
            "minLength": 2,
            "maxLength": 30,
            "pattern": "^[A-Za-z0-9_-]+$",
            "description": "Trimmed and upper-cased, then 2 to 30 characters of A-Z, 0-9, hyphen or underscore. Unique within the store."
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "percentage",
              "fixed"
            ],
            "description": "Case-insensitive. There is no default: a missing or unknown value is rejected."
          },
          "discount_value": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Greater than 0. At most 100 for `percentage`. A 100% discount needs a confirmation (see confirm_token). Changing either discount_type or discount_value re-checks the pair."
          },
          "min_order_amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Minimum subtotal in DZD. 0, an empty string or null means no minimum."
          },
          "max_uses": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Integer 1 or more. 0, an empty string or null means unlimited."
          },
          "is_active": {
            "type": "boolean",
            "default": true
          },
          "starts_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Any date-time string the server can parse; stored as `YYYY-MM-DD HH:MM:SS`, Africa/Algiers time when no offset is given. Empty or null clears it."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Same format as starts_at. When sent it must be in the future and after starts_at. Empty or null clears it."
          },
          "confirm_token": {
            "type": "string",
            "description": "Token returned in a `confirmation_required` or `confirmation_stale` error. Send it back after the merchant approves a 100% discount. Single use, valid 600 seconds."
          },
          "confirm_full_discount": {
            "type": "boolean",
            "description": "Approves a 100% discount without a token. Not accepted from the in-dashboard assistant, which must use confirm_token."
          }
        }
      },
      "PromoCodeUpdate": {
        "type": "object",
        "description": "Partial update: only the keys you send are changed. used_count cannot be written.",
        "properties": {
          "code": {
            "type": "string",
            "minLength": 2,
            "maxLength": 30,
            "pattern": "^[A-Za-z0-9_-]+$",
            "description": "Trimmed and upper-cased, then 2 to 30 characters of A-Z, 0-9, hyphen or underscore. Unique within the store."
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "percentage",
              "fixed"
            ],
            "description": "Case-insensitive. There is no default: a missing or unknown value is rejected."
          },
          "discount_value": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Greater than 0. At most 100 for `percentage`. A 100% discount needs a confirmation (see confirm_token). Changing either discount_type or discount_value re-checks the pair."
          },
          "min_order_amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Minimum subtotal in DZD. 0, an empty string or null means no minimum."
          },
          "max_uses": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Integer 1 or more. 0, an empty string or null means unlimited."
          },
          "is_active": {
            "type": "boolean"
          },
          "starts_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Any date-time string the server can parse; stored as `YYYY-MM-DD HH:MM:SS`, Africa/Algiers time when no offset is given. Empty or null clears it."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Same format as starts_at. When sent it must be in the future and after starts_at. Empty or null clears it."
          },
          "confirm_token": {
            "type": "string",
            "description": "Token returned in a `confirmation_required` or `confirmation_stale` error. Send it back after the merchant approves a 100% discount. Single use, valid 600 seconds."
          },
          "confirm_full_discount": {
            "type": "boolean",
            "description": "Approves a 100% discount without a token. Not accepted from the in-dashboard assistant, which must use confirm_token."
          }
        }
      },
      "QuotaOverride": {
        "type": "object",
        "additionalProperties": true,
        "description": "Per-store override set by support; row pass-through, so more fields may appear. A null limit falls back to the tier value.",
        "properties": {
          "store_id": {
            "type": "integer"
          },
          "requests_per_month": {
            "type": [
              "integer",
              "null"
            ]
          },
          "signups_per_month": {
            "type": [
              "integer",
              "null"
            ]
          },
          "webhooks_per_month": {
            "type": [
              "integer",
              "null"
            ]
          },
          "requests_per_minute": {
            "type": [
              "integer",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "set_by_user_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "updated_at": {
            "type": "string",
            "description": "Date and time as `YYYY-MM-DD HH:MM:SS`, store server local time."
          }
        }
      },
      "Quotas": {
        "type": "object",
        "required": [
          "tier",
          "tier_limits",
          "overrides",
          "effective"
        ],
        "properties": {
          "tier": {
            "type": "string"
          },
          "tier_limits": {
            "anyOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/UsageLimits"
                  }
                ],
                "additionalProperties": true
              },
              {
                "type": "array",
                "maxItems": 0
              }
            ],
            "description": "The tier's configured limits, passed through as stored. An empty array when the tier has no configuration."
          },
          "overrides": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/QuotaOverride"
              },
              {
                "type": "null"
              }
            ]
          },
          "effective": {
            "$ref": "#/components/schemas/UsageLimits"
          }
        }
      },
      "ShippingConfirmInput": {
        "type": "object",
        "description": "Confirmation for an action that asks for one.",
        "properties": {
          "confirm_token": {
            "type": "string",
            "description": "The token from the `confirmation_required` or `confirmation_stale` error, sent after the merchant approved the summary."
          },
          "confirm": {
            "type": "boolean",
            "description": "Accepted instead of a token from keys that are not used by the in-dashboard assistant. Must be the JSON value true."
          }
        }
      },
      "ShippingCoverage": {
        "type": "object",
        "required": [
          "provider",
          "is_send_default",
          "knowledge_synced_at",
          "wilayas"
        ],
        "description": "`wilaya_id`, `desk_send_allowed`, `communes` and `desks` are present only when the request named a wilaya.",
        "properties": {
          "provider": {
            "type": "string"
          },
          "is_send_default": {
            "type": "boolean"
          },
          "knowledge_synced_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Timestamp as `YYYY-MM-DD HH:MM:SS`."
          },
          "wilayas": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ShippingCoverageWilaya"
            }
          },
          "wilaya_id": {
            "type": "integer"
          },
          "desk_send_allowed": {
            "type": "boolean",
            "description": "Whether send-to-delivery accepts a desk order to this wilaya with this courier."
          },
          "communes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ShippingCoverageCommune"
            }
          },
          "desks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ShippingCoverageDesk"
            }
          }
        }
      },
      "ShippingCoverageCommune": {
        "type": "object",
        "required": [
          "commune_id",
          "name",
          "name_ar",
          "home",
          "desk"
        ],
        "properties": {
          "commune_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "French name, or the courier's own name when the commune is not matched."
          },
          "name_ar": {
            "type": [
              "string",
              "null"
            ]
          },
          "home": {
            "type": "boolean"
          },
          "desk": {
            "type": "boolean"
          }
        }
      },
      "ShippingCoverageDesk": {
        "type": "object",
        "required": [
          "desk_id",
          "name",
          "address",
          "phone",
          "commune_id"
        ],
        "properties": {
          "desk_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The courier's desk id."
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "commune_id": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "ShippingCoverageWilaya": {
        "type": "object",
        "required": [
          "wilaya_id",
          "name",
          "communes",
          "communes_home",
          "communes_desk",
          "desks"
        ],
        "properties": {
          "wilaya_id": {
            "type": "integer"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "French name."
          },
          "communes": {
            "type": "integer"
          },
          "communes_home": {
            "type": "integer"
          },
          "communes_desk": {
            "type": "integer"
          },
          "desks": {
            "type": "integer"
          }
        }
      },
      "ShippingCredentialField": {
        "type": "object",
        "required": [
          "label",
          "required"
        ],
        "properties": {
          "label": {
            "type": "string",
            "description": "What this courier calls the value."
          },
          "required": {
            "type": "boolean"
          }
        }
      },
      "ShippingProvider": {
        "type": "object",
        "required": [
          "provider",
          "family",
          "credentials",
          "extra_fields",
          "supports_rate_sync",
          "linked",
          "source",
          "is_enabled",
          "is_default",
          "is_send_default",
          "has_id",
          "has_token",
          "delivery_tier",
          "economic_available",
          "credentials_failed_at",
          "synced_tier",
          "stock_account",
          "auto_validate",
          "custom_name",
          "linked_at",
          "updated_at"
        ],
        "properties": {
          "provider": {
            "type": "string",
            "description": "Courier slug."
          },
          "family": {
            "type": "string",
            "enum": [
              "yalidine",
              "procolis",
              "ecotrack",
              "standalone"
            ]
          },
          "credentials": {
            "type": "object",
            "required": [
              "api_id",
              "_note"
            ],
            "description": "What `api_id` and `api_token` mean for this courier. `api_token` is absent for couriers that take one value.",
            "properties": {
              "api_id": {
                "$ref": "#/components/schemas/ShippingCredentialField"
              },
              "api_token": {
                "$ref": "#/components/schemas/ShippingCredentialField"
              },
              "_note": {
                "type": "string"
              }
            }
          },
          "extra_fields": {
            "description": "Optional or required extra link fields for this courier, keyed by field name (`api_url`, `custom_name`, `stock_account`, `auto_validate`, `delivery_tier`). An empty array when there are none.",
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": {
                  "$ref": "#/components/schemas/ShippingProviderExtraField"
                }
              },
              {
                "type": "array",
                "maxItems": 0
              }
            ]
          },
          "supports_rate_sync": {
            "type": "boolean"
          },
          "linked": {
            "type": "boolean"
          },
          "source": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "store_delivery_providers",
              "store_row",
              null
            ],
            "description": "`store_delivery_providers` when the courier was added from the provider list, `store_row` when it is configured directly on the store settings, null when not linked."
          },
          "is_enabled": {
            "type": "boolean"
          },
          "is_default": {
            "type": "boolean"
          },
          "is_send_default": {
            "type": "boolean",
            "description": "The courier send-to-delivery uses when the caller names none."
          },
          "has_id": {
            "type": "boolean"
          },
          "has_token": {
            "type": "boolean"
          },
          "delivery_tier": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "express",
              "economic",
              null
            ]
          },
          "economic_available": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the courier account offers the economic tier, as last seen by a rate sync."
          },
          "credentials_failed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601. Set when the courier kept rejecting the stored credentials; sends are refused until the courier is linked again."
          },
          "synced_tier": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "express",
              "economic",
              null
            ]
          },
          "stock_account": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "auto_validate": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "custom_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "linked_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Timestamp as `YYYY-MM-DD HH:MM:SS`. Null for a courier configured on the store settings."
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Timestamp as `YYYY-MM-DD HH:MM:SS`."
          }
        }
      },
      "ShippingProviderDefaultInput": {
        "type": "object",
        "required": [
          "provider"
        ],
        "properties": {
          "provider": {
            "type": "string",
            "description": "Slug of a courier added from the provider list."
          }
        }
      },
      "ShippingProviderDefaultResult": {
        "type": "object",
        "required": [
          "provider",
          "is_default",
          "previous_default",
          "undoable",
          "warning"
        ],
        "properties": {
          "provider": {
            "type": "string"
          },
          "is_default": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "previous_default": {
            "type": [
              "string",
              "null"
            ]
          },
          "undoable": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "warning": {
            "type": "string"
          }
        }
      },
      "ShippingProviderExtraField": {
        "type": "object",
        "required": [
          "label",
          "required"
        ],
        "properties": {
          "label": {
            "type": "string"
          },
          "required": {
            "type": "boolean"
          },
          "note": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "boolean"
            ]
          },
          "max_length": {
            "type": "integer"
          },
          "values": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "express",
                "economic"
              ]
            }
          }
        }
      },
      "ShippingProviderLinkInput": {
        "type": "object",
        "required": [
          "provider"
        ],
        "allOf": [
          {
            "$ref": "#/components/schemas/ShippingProviderTestInput"
          }
        ],
        "properties": {
          "custom_name": {
            "type": "string",
            "description": "customecotrack only, required there. HTML is stripped; cut to 100 characters."
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "set_default": {
            "type": "boolean",
            "default": false,
            "description": "Make this courier the store default."
          },
          "stock_account": {
            "type": "boolean",
            "description": "Ecotrack couriers: fulfil from courier stock. Omitted keeps the stored value."
          },
          "auto_validate": {
            "type": "boolean",
            "description": "noest: auto-validate orders. Omitted keeps the stored value."
          }
        }
      },
      "ShippingProviderLinkResult": {
        "type": "object",
        "required": [
          "provider",
          "is_enabled",
          "is_default",
          "has_id",
          "has_token",
          "undoable",
          "note"
        ],
        "properties": {
          "provider": {
            "type": "string",
            "description": "The slug saved. A zrexpressnew link accepted by the older ZR Express platform is saved as zrexpress."
          },
          "is_enabled": {
            "type": "boolean"
          },
          "is_default": {
            "type": "boolean"
          },
          "has_id": {
            "type": "boolean"
          },
          "has_token": {
            "type": "boolean"
          },
          "undoable": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "note": {
            "type": "string"
          }
        }
      },
      "ShippingProviderList": {
        "type": "object",
        "required": [
          "count",
          "note",
          "providers"
        ],
        "properties": {
          "count": {
            "type": "integer"
          },
          "note": {
            "type": "string"
          },
          "providers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ShippingProvider"
            }
          }
        }
      },
      "ShippingProviderTestInput": {
        "type": "object",
        "required": [
          "provider"
        ],
        "properties": {
          "provider": {
            "type": "string",
            "description": "Courier slug from GET /v1/shipping/providers."
          },
          "api_id": {
            "type": "string",
            "description": "First credential. Empty or omitted uses the value stored for this courier."
          },
          "api_token": {
            "type": "string",
            "description": "Second credential, for couriers that take two. Empty or omitted uses the stored value."
          },
          "api_url": {
            "type": "string",
            "description": "customecotrack only, required there: an https Ecotrack tenant URL on a public host. When the stored credentials are reused it must match the stored URL."
          },
          "delivery_tier": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "express",
              "economic",
              null
            ],
            "description": "economic is accepted for guepex only."
          }
        }
      },
      "ShippingProviderTestResult": {
        "type": "object",
        "required": [
          "provider",
          "ok",
          "message",
          "resolved_provider",
          "saved"
        ],
        "properties": {
          "provider": {
            "type": "string"
          },
          "ok": {
            "type": "boolean",
            "description": "True when the courier accepted the credentials."
          },
          "message": {
            "type": "string",
            "description": "The courier's own answer."
          },
          "resolved_provider": {
            "type": [
              "string",
              "null"
            ],
            "description": "For zrexpressnew, which ZR Express platform accepted the pair (`zrexpressnew` or `zrexpress`); null otherwise."
          },
          "saved": {
            "type": "boolean",
            "enum": [
              false
            ]
          }
        }
      },
      "ShippingProviderUnlinkResult": {
        "type": "object",
        "required": [
          "provider",
          "unlinked",
          "undoable",
          "remaining_providers",
          "new_default",
          "send_to_delivery_active",
          "warning"
        ],
        "properties": {
          "provider": {
            "type": "string"
          },
          "unlinked": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "undoable": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "remaining_providers": {
            "type": "integer"
          },
          "new_default": {
            "type": [
              "string",
              "null"
            ]
          },
          "send_to_delivery_active": {
            "type": "boolean",
            "description": "False when no courier is left and send-to-delivery is off."
          },
          "warning": {
            "type": "string"
          }
        }
      },
      "ShippingRate": {
        "type": "object",
        "description": "One wilaya's delivery rate as stored.",
        "required": [
          "home_price",
          "home_enabled",
          "desk_price",
          "desk_enabled",
          "days",
          "is_active",
          "synced_provider",
          "synced_at"
        ],
        "properties": {
          "home_price": {
            "type": "number",
            "description": "Home delivery price in DZD."
          },
          "home_enabled": {
            "type": "boolean"
          },
          "desk_price": {
            "type": "number",
            "description": "Stop-desk delivery price in DZD."
          },
          "desk_enabled": {
            "type": "boolean"
          },
          "days": {
            "type": "integer",
            "description": "Delivery time in days."
          },
          "is_active": {
            "type": "boolean"
          },
          "synced_provider": {
            "type": [
              "string",
              "null"
            ],
            "description": "Courier whose price list last wrote this rate; null when set by hand."
          },
          "synced_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "When a courier sync last wrote this rate. Timestamp as `YYYY-MM-DD HH:MM:SS`."
          }
        }
      },
      "ShippingRateInput": {
        "type": "object",
        "description": "Fields to set for one wilaya. Every field is optional.",
        "properties": {
          "home_price": {
            "description": "DZD, rounded to two decimals, 0 to 100000. A numeric string is accepted. Null or omitted keeps the stored value.",
            "type": [
              "number",
              "string",
              "null"
            ]
          },
          "home_enabled": {
            "description": "true or false; 0, 1, \"0\" and \"1\" are also accepted. Null or omitted keeps the stored value.",
            "type": [
              "boolean",
              "integer",
              "string",
              "null"
            ]
          },
          "desk_price": {
            "description": "DZD, rounded to two decimals, 0 to 100000. A numeric string is accepted. Null or omitted keeps the stored value.",
            "type": [
              "number",
              "string",
              "null"
            ]
          },
          "desk_enabled": {
            "description": "true or false; 0, 1, \"0\" and \"1\" are also accepted. Null or omitted keeps the stored value.",
            "type": [
              "boolean",
              "integer",
              "string",
              "null"
            ]
          },
          "days": {
            "type": [
              "integer",
              "string",
              "null"
            ],
            "description": "Whole number of days, 0 to 60. Null or omitted keeps the stored value (3 for a new wilaya)."
          }
        }
      },
      "ShippingRateSaved": {
        "type": "object",
        "description": "One wilaya's rate after the write.",
        "required": [
          "home_price",
          "home_enabled",
          "desk_price",
          "desk_enabled",
          "days"
        ],
        "properties": {
          "home_price": {
            "type": "number"
          },
          "home_enabled": {
            "type": "boolean"
          },
          "desk_price": {
            "type": "number"
          },
          "desk_enabled": {
            "type": "boolean"
          },
          "days": {
            "type": "integer"
          }
        }
      },
      "ShippingRateTable": {
        "type": "object",
        "required": [
          "wilaya_mode",
          "currency",
          "limits",
          "count",
          "rates"
        ],
        "properties": {
          "wilaya_mode": {
            "type": "string",
            "enum": [
              "58",
              "69"
            ]
          },
          "currency": {
            "type": "string",
            "enum": [
              "DZD"
            ]
          },
          "limits": {
            "type": "object",
            "required": [
              "max_price",
              "max_delivery_days"
            ],
            "properties": {
              "max_price": {
                "type": "number",
                "description": "Highest accepted price, 100000."
              },
              "max_delivery_days": {
                "type": "integer",
                "description": "Highest accepted `days`, 60."
              }
            }
          },
          "count": {
            "type": "integer",
            "description": "Number of wilayas with a rate."
          },
          "rates": {
            "type": "object",
            "description": "Keyed by wilaya id.",
            "propertyNames": {
              "pattern": "^[0-9]+$"
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/ShippingRate"
            }
          }
        }
      },
      "ShippingRatesInput": {
        "type": "object",
        "required": [
          "rates"
        ],
        "properties": {
          "rates": {
            "type": "object",
            "minProperties": 1,
            "maxProperties": 69,
            "description": "Map of wilaya id to the fields to set. Keys are plain integer ids with no leading zero.",
            "propertyNames": {
              "pattern": "^[0-9]+$"
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/ShippingRateInput"
            }
          }
        }
      },
      "ShippingRatesSaved": {
        "type": "object",
        "required": [
          "updated",
          "wilaya_ids",
          "rates"
        ],
        "properties": {
          "updated": {
            "type": "integer",
            "description": "Number of wilayas written."
          },
          "wilaya_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "rates": {
            "type": "object",
            "description": "The written wilayas only, keyed by wilaya id.",
            "propertyNames": {
              "pattern": "^[0-9]+$"
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/ShippingRateSaved"
            }
          }
        }
      },
      "ShippingRatesSyncInput": {
        "type": "object",
        "required": [
          "provider"
        ],
        "allOf": [
          {
            "$ref": "#/components/schemas/ShippingConfirmInput"
          }
        ],
        "properties": {
          "provider": {
            "type": "string",
            "description": "Slug of a linked, enabled courier."
          }
        }
      },
      "ShippingRatesSyncResult": {
        "type": "object",
        "required": [
          "status",
          "sync_id",
          "change_id",
          "note"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "already_running"
            ]
          },
          "sync_id": {
            "type": "string"
          },
          "change_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Change to undo to get the prior prices back. Null when a sync was already running."
          },
          "note": {
            "type": "string"
          }
        }
      },
      "ShippingSettings": {
        "type": "object",
        "required": [
          "free_shipping",
          "free_shipping_threshold",
          "free_shipping_threshold_active",
          "wilaya_mode"
        ],
        "properties": {
          "free_shipping": {
            "type": "boolean"
          },
          "free_shipping_threshold": {
            "type": [
              "number",
              "null"
            ],
            "description": "Order amount in DZD above which shipping is free. 0 or null means no threshold."
          },
          "free_shipping_threshold_active": {
            "type": "boolean",
            "description": "True only when the threshold is above 0."
          },
          "wilaya_mode": {
            "type": "string",
            "enum": [
              "58",
              "69"
            ]
          },
          "notes": {
            "type": "object",
            "description": "Plain-text explanations. Returned by GET only.",
            "properties": {
              "free_shipping_threshold": {
                "type": "string"
              },
              "wilaya_mode": {
                "type": "string"
              }
            }
          }
        }
      },
      "ShippingSettingsUpdate": {
        "type": "object",
        "minProperties": 1,
        "description": "Send at least one of the three fields.",
        "properties": {
          "free_shipping": {
            "type": [
              "boolean",
              "integer",
              "string"
            ],
            "description": "true or false; 0, 1, \"0\" and \"1\" are also accepted."
          },
          "free_shipping_threshold": {
            "type": [
              "number",
              "string",
              "null"
            ],
            "description": "DZD, rounded to two decimals, 0 to 99999999.99. 0 or null disables the threshold."
          },
          "wilaya_mode": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Only \"58\" is accepted."
          }
        }
      },
      "Store": {
        "type": "object",
        "description": "Store profile.",
        "required": [
          "id",
          "name",
          "slug",
          "language",
          "description",
          "logo",
          "favicon",
          "banner",
          "theme",
          "subdomain",
          "custom_domain",
          "custom_domain_verified",
          "public_url",
          "hide_branding",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "description": "Storefront language, `ar` or `fr`."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "logo": {
            "type": [
              "string",
              "null"
            ],
            "description": "Image path, as stored."
          },
          "favicon": {
            "type": [
              "string",
              "null"
            ]
          },
          "banner": {
            "type": [
              "string",
              "null"
            ]
          },
          "theme": {
            "type": "object",
            "required": [
              "primary_color",
              "secondary_color",
              "background_color",
              "font_family"
            ],
            "properties": {
              "primary_color": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "secondary_color": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "background_color": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "font_family": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "subdomain": {
            "type": [
              "string",
              "null"
            ],
            "description": "`<slug>.<domain>`; null when either part is missing."
          },
          "custom_domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "custom_domain_verified": {
            "type": "boolean"
          },
          "public_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "A verified custom domain first, else the subdomain; null when neither exists."
          },
          "hide_branding": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "description": "`YYYY-MM-DD HH:MM:SS`, Algiers time."
          }
        }
      },
      "StoreDesign": {
        "type": "object",
        "required": [
          "theme",
          "plan",
          "sections"
        ],
        "properties": {
          "theme": {
            "type": "string",
            "description": "Key of the theme the store uses."
          },
          "plan": {
            "type": "string",
            "description": "Plan the checks ran against; an expired paid plan counts as `free`."
          },
          "sections": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "key",
                "name",
                "plan_required",
                "page",
                "fields"
              ],
              "properties": {
                "key": {
                  "type": "string"
                },
                "name": {
                  "description": "Section label from the theme manifest."
                },
                "plan_required": {
                  "type": "string"
                },
                "page": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "fields": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/StoreDesignField"
                  }
                }
              }
            }
          }
        }
      },
      "StoreDesignField": {
        "type": "object",
        "required": [
          "key",
          "type",
          "plan_required",
          "writable",
          "locked"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Field name to send in `PATCH /v1/store/design`."
          },
          "type": {
            "type": "string",
            "description": "Control type from the theme manifest (defaults to `text`)."
          },
          "plan_required": {
            "type": "string",
            "description": "Lowest plan that unlocks the field (`free` when open to all)."
          },
          "writable": {
            "type": "boolean",
            "description": "False when the API does not accept this field."
          },
          "locked": {
            "type": "boolean",
            "description": "True when this store plan cannot write it."
          },
          "allowed_values": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Present on fixed-choice fields only."
          },
          "value": {
            "description": "Current value; present only on `GET /v1/store/design`. Switches come back as booleans."
          }
        }
      },
      "StoreDesignUpdate": {
        "type": "object",
        "minProperties": 1,
        "additionalProperties": true,
        "description": "Design fields. Send only what changes; unknown keys are ignored. Some fields are absent from a given theme manifest but still stored.",
        "properties": {
          "store_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters."
          },
          "show_store_name": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 5000,
            "description": "Plain text (HTML tags removed), cut to 5000 characters."
          },
          "primary_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; null clears it, an invalid value is stored as `#f59e0b`."
          },
          "secondary_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; null clears it, an invalid value is stored as `#fbbf24`."
          },
          "background_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; null clears it, an invalid value is stored as `#ffffff`. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "section_titles_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; null clears it, an invalid value is stored as `#1f2937`. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "navbar_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; null clears it, an invalid value is stored as `#ffffff`. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "footer_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; null clears it, an invalid value is stored as `#1f2937`. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "font_family": {
            "type": "string",
            "description": "Letters, digits, spaces and hyphens only; any other value is stored as `Cairo`."
          },
          "store_language": {
            "type": "string",
            "enum": [
              "ar",
              "fr"
            ],
            "description": "Any other value is stored as `ar`."
          },
          "hide_branding": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan. Enabling it needs the unlimited or enterprise plan; a pro store may only turn it off."
          },
          "theme_style": {
            "type": "string",
            "maxLength": 255,
            "description": "Trimmed and cut to 255 characters."
          },
          "button_style": {
            "type": "string",
            "maxLength": 255,
            "description": "Trimmed and cut to 255 characters."
          },
          "card_style": {
            "type": "string",
            "maxLength": 255,
            "description": "Trimmed and cut to 255 characters."
          },
          "show_announcement": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "announcement_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "Plain text (HTML tags removed), cut to 500 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "announcement_link": {
            "type": "string",
            "maxLength": 255,
            "description": "Link: https, http, mailto, tel, a /path, an #anchor or a bare handle. Any other scheme is refused with `invalid_url`. Cut to 255 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "announcement_bg_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; an empty or invalid value clears the override. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "announcement_text_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; an empty or invalid value clears the override. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "announcement_animation": {
            "type": "string",
            "enum": [
              "none",
              "marquee"
            ],
            "description": "Any other value is stored as `none`. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "show_hero": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "hero_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Plain text (HTML tags removed), cut to 255 characters."
          },
          "hero_subtitle": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1000,
            "description": "Plain text (HTML tags removed), cut to 1000 characters."
          },
          "hero_button_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters."
          },
          "hero_button_link": {
            "type": "string",
            "maxLength": 255,
            "description": "Link: https, http, mailto, tel, a /path, an #anchor or a bare handle. Any other scheme is refused with `invalid_url`. Cut to 255 characters."
          },
          "show_categories": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "categories_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Plain text (HTML tags removed), cut to 255 characters."
          },
          "categories_layout": {
            "type": "string",
            "maxLength": 255,
            "description": "Trimmed and cut to 255 characters."
          },
          "categories_subtitle": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "Plain text (HTML tags removed), cut to 500 characters."
          },
          "show_featured": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "featured_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Plain text (HTML tags removed), cut to 255 characters."
          },
          "featured_subtitle": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1000,
            "description": "Plain text (HTML tags removed), cut to 1000 characters."
          },
          "banner_button_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters."
          },
          "banner_button_link": {
            "type": "string",
            "maxLength": 255,
            "description": "Link: https, http, mailto, tel, a /path, an #anchor or a bare handle. Any other scheme is refused with `invalid_url`. Cut to 255 characters."
          },
          "products_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Plain text (HTML tags removed), cut to 255 characters."
          },
          "products_per_page": {
            "type": "integer",
            "minimum": 4,
            "maximum": 48,
            "description": "Clamped to 4-48."
          },
          "products_layout": {
            "type": "string",
            "maxLength": 255,
            "description": "Trimmed and cut to 255 characters."
          },
          "sort_out_of_stock_last": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "marquee_speed": {
            "type": "string",
            "enum": [
              "slow",
              "normal",
              "fast"
            ],
            "description": "Any other value is stored as `normal`."
          },
          "show_trust": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "trust1_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 60,
            "description": "Plain text (HTML tags removed), cut to 60 characters."
          },
          "trust1_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80,
            "description": "Plain text (HTML tags removed), cut to 80 characters."
          },
          "trust2_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 60,
            "description": "Plain text (HTML tags removed), cut to 60 characters."
          },
          "trust2_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80,
            "description": "Plain text (HTML tags removed), cut to 80 characters."
          },
          "trust3_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 60,
            "description": "Plain text (HTML tags removed), cut to 60 characters."
          },
          "trust3_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80,
            "description": "Plain text (HTML tags removed), cut to 80 characters."
          },
          "footer_about": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000,
            "description": "Plain text (HTML tags removed), cut to 2000 characters."
          },
          "footer_copyright": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Plain text (HTML tags removed), cut to 255 characters."
          },
          "show_social_links": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "show_search": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "facebook": {
            "type": "string",
            "maxLength": 255,
            "description": "Link: https, http, mailto, tel, a /path, an #anchor or a bare handle. Any other scheme is refused with `invalid_url`. Cut to 255 characters."
          },
          "instagram": {
            "type": "string",
            "maxLength": 255,
            "description": "Link: https, http, mailto, tel, a /path, an #anchor or a bare handle. Any other scheme is refused with `invalid_url`. Cut to 255 characters."
          },
          "whatsapp": {
            "type": "string",
            "maxLength": 20,
            "description": "Link: https, http, mailto, tel, a /path, an #anchor or a bare handle. Any other scheme is refused with `invalid_url`. Cut to 20 characters."
          },
          "tiktok": {
            "type": "string",
            "maxLength": 255,
            "description": "Link: https, http, mailto, tel, a /path, an #anchor or a bare handle. Any other scheme is refused with `invalid_url`. Cut to 255 characters."
          },
          "telegram": {
            "type": "string",
            "maxLength": 255,
            "description": "Link: https, http, mailto, tel, a /path, an #anchor or a bare handle. Any other scheme is refused with `invalid_url`. Cut to 255 characters."
          },
          "github": {
            "type": "string",
            "maxLength": 255,
            "description": "Link: https, http, mailto, tel, a /path, an #anchor or a bare handle. Any other scheme is refused with `invalid_url`. Cut to 255 characters."
          },
          "custom_link_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters."
          },
          "custom_link_url": {
            "type": "string",
            "maxLength": 255,
            "description": "Link: https, http, mailto, tel, a /path, an #anchor or a bare handle. Any other scheme is refused with `invalid_url`. Cut to 255 characters."
          },
          "seo_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Plain text (HTML tags removed), cut to 255 characters."
          },
          "seo_description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "Plain text (HTML tags removed), cut to 500 characters."
          },
          "seo_keywords": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "Plain text (HTML tags removed), cut to 500 characters."
          },
          "google_analytics_id": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "Plain text (HTML tags removed), cut to 50 characters."
          },
          "facebook_pixel_id": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "Plain text (HTML tags removed), cut to 50 characters."
          },
          "custom_css": {
            "type": [
              "string",
              "null"
            ],
            "description": "Enterprise plan only; skipped on any other plan. Sanitized before it is stored."
          },
          "navbar_style": {
            "type": "string",
            "maxLength": 255,
            "description": "Trimmed and cut to 255 characters."
          },
          "navbar_sticky": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "navbar_shadow": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "navbar_border": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "navbar_text_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; null clears it, an invalid value is stored as `#1f2937`. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "navbar_logo_size": {
            "type": "string",
            "maxLength": 255,
            "description": "Trimmed and cut to 255 characters."
          },
          "navbar_menu_style": {
            "type": "string",
            "maxLength": 255,
            "description": "Trimmed and cut to 255 characters."
          },
          "show_cart_icon": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "cart_icon_style": {
            "type": "string",
            "maxLength": 255,
            "description": "Trimmed and cut to 255 characters."
          },
          "hide_add_to_cart": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "hide_buy_now": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "hide_product_cart": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "hide_product_navbar": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "show_fast_checkout": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "fast_checkout_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "fast_checkout_button_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "fast_checkout_bg_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; null clears it, an invalid value is stored as `#ffffff`. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "hide_fast_checkout_address": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "fc_show_notes": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "fc_single_column": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "fc_show_icons": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "show_related_products": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "card_hide_add_to_cart": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "card_hide_category": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "card_hide_price": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "card_hide_compare_price": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "card_hide_badge": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "card_hide_hover_action": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "card_border_radius": {
            "type": "string",
            "enum": [
              "0px",
              "8px",
              "16px",
              "24px"
            ],
            "description": "Any other value is stored as `16px`."
          },
          "product_image_layout": {
            "type": "string",
            "enum": [
              "carousel",
              "stacked"
            ],
            "description": "Any other value is stored as `carousel`."
          },
          "product_image_cadre": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "card_button_action": {
            "type": "string",
            "enum": [
              "add_to_cart",
              "buy_now"
            ],
            "description": "Any other value is stored as `add_to_cart`."
          },
          "card_button_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text, cut to 100 characters; empty clears it."
          },
          "card_button_style": {
            "type": "string",
            "enum": [
              "label",
              "icon"
            ],
            "description": "Any other value is stored as `label`."
          },
          "product_buy_now_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; an empty or invalid value clears the override. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "fc_button_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; an empty or invalid value clears the override. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_page_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_personal_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_address_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_delivery_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_summary_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_button_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Plain text (HTML tags removed), cut to 100 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_payment_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255,
            "description": "Plain text (HTML tags removed), cut to 255 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_hide_email": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "checkout_hide_notes": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "checkout_header_bg_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; null clears it, an invalid value is stored as `#ffffff`. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_hide_address": {
            "type": "boolean",
            "description": "Any truthy value is stored as on, anything else as off."
          },
          "checkout_success_message": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "Plain text (HTML tags removed), cut to 500 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_layout": {
            "type": "string",
            "enum": [
              "default",
              "compact"
            ],
            "description": "Any other value is stored as `default`. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_banner_text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "Plain text (HTML tags removed), cut to 500 characters. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          },
          "checkout_banner_bg_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "description": "Hex colour `#RRGGBB`; null clears it, an invalid value is stored as `#ffffff`. Skipped (listed in `skipped`) unless the store is on the pro, unlimited or enterprise plan."
          }
        }
      },
      "StoreDesignUpdateResult": {
        "type": "object",
        "required": [
          "updated",
          "skipped",
          "values"
        ],
        "properties": {
          "updated": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "skipped": {
            "type": [
              "object",
              "array"
            ],
            "additionalProperties": {
              "type": "string"
            },
            "description": "Field name to the reason it was not written, for example `requires a paid plan`. Empty array when nothing was skipped."
          },
          "values": {
            "type": "object",
            "additionalProperties": true,
            "description": "Each written field with the stored value; switches as 1 or 0."
          }
        }
      },
      "StoreFastCheckoutThemeResult": {
        "type": "object",
        "required": [
          "fast_checkout_theme"
        ],
        "properties": {
          "fast_checkout_theme": {
            "type": "string"
          }
        }
      },
      "StoreHomeSectionSettings": {
        "type": [
          "object",
          "array"
        ],
        "additionalProperties": true,
        "description": "Stored home page section settings (row pass-through: keys saved from the dashboard may also appear). Switches are stored as 1 or 0. Comes back as an empty JSON array `[]` when nothing was ever saved.",
        "properties": {
          "show_hero_slider": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_sidebar_widget": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_category_cards": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_top_sellers": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_popular_by_category": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_promo_banners": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_multi_column_lists": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "sidebar_widget_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "top_sellers_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "popular_by_category_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "sidebar_widget_category_id": {
            "type": "integer",
            "description": "A category of this store; 0 or less clears it. A category of another store is refused with `invalid_category`."
          },
          "popular_categories": {
            "type": "array",
            "items": {},
            "description": "Stored as sent; a non-array value is stored as an empty array."
          },
          "column_1_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "column_1_category_id": {
            "type": "integer",
            "description": "A category of this store; 0 or less clears it. A category of another store is refused with `invalid_category`."
          },
          "column_2_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "column_2_category_id": {
            "type": "integer",
            "description": "A category of this store; 0 or less clears it. A category of another store is refused with `invalid_category`."
          },
          "column_3_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "column_3_category_id": {
            "type": "integer",
            "description": "A category of this store; 0 or less clears it. A category of another store is refused with `invalid_category`."
          },
          "column_4_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "column_4_category_id": {
            "type": "integer",
            "description": "A category of this store; 0 or less clears it. A category of another store is refused with `invalid_category`."
          },
          "banner_1_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_1_subtitle": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_1_button_text": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_1_button_link": {
            "type": "string",
            "maxLength": 500,
            "description": "Link: https, http, mailto, tel, a /path or an #anchor; any other scheme is refused with `invalid_url`."
          },
          "banner_1_image": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_2_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_2_subtitle": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_2_button_text": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_2_button_link": {
            "type": "string",
            "maxLength": 500,
            "description": "Link: https, http, mailto, tel, a /path or an #anchor; any other scheme is refused with `invalid_url`."
          },
          "banner_2_image": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "hero_slides": {
            "type": "array",
            "items": {},
            "description": "Stored as sent; a non-array value is stored as an empty array."
          },
          "section_order": {
            "type": "array",
            "items": {},
            "description": "Stored as sent; a non-array value is stored as an empty array."
          },
          "show_testimonials": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "testimonials_title": {
            "type": "string",
            "maxLength": 200,
            "description": "Plain text, cut to 200 characters."
          },
          "testimonials": {
            "type": "array",
            "maxItems": 24,
            "items": {
              "$ref": "#/components/schemas/StoreTestimonial"
            },
            "description": "At most 24 are kept; entries with neither a name nor a comment are dropped."
          }
        }
      },
      "StoreHomeSections": {
        "type": "object",
        "required": [
          "theme",
          "settings"
        ],
        "properties": {
          "theme": {
            "type": "string"
          },
          "settings": {
            "$ref": "#/components/schemas/StoreHomeSectionSettings"
          }
        }
      },
      "StoreHomeSectionsUpdate": {
        "type": "object",
        "minProperties": 1,
        "additionalProperties": true,
        "description": "Home section keys to merge. Unknown keys are ignored.",
        "properties": {
          "show_hero_slider": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_sidebar_widget": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_category_cards": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_top_sellers": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_popular_by_category": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_promo_banners": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "show_multi_column_lists": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "sidebar_widget_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "top_sellers_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "popular_by_category_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "sidebar_widget_category_id": {
            "type": "integer",
            "description": "A category of this store; 0 or less clears it. A category of another store is refused with `invalid_category`."
          },
          "popular_categories": {
            "type": "array",
            "items": {},
            "description": "Stored as sent; a non-array value is stored as an empty array."
          },
          "column_1_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "column_1_category_id": {
            "type": "integer",
            "description": "A category of this store; 0 or less clears it. A category of another store is refused with `invalid_category`."
          },
          "column_2_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "column_2_category_id": {
            "type": "integer",
            "description": "A category of this store; 0 or less clears it. A category of another store is refused with `invalid_category`."
          },
          "column_3_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "column_3_category_id": {
            "type": "integer",
            "description": "A category of this store; 0 or less clears it. A category of another store is refused with `invalid_category`."
          },
          "column_4_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "column_4_category_id": {
            "type": "integer",
            "description": "A category of this store; 0 or less clears it. A category of another store is refused with `invalid_category`."
          },
          "banner_1_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_1_subtitle": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_1_button_text": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_1_button_link": {
            "type": "string",
            "maxLength": 500,
            "description": "Link: https, http, mailto, tel, a /path or an #anchor; any other scheme is refused with `invalid_url`."
          },
          "banner_1_image": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_2_title": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_2_subtitle": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_2_button_text": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "banner_2_button_link": {
            "type": "string",
            "maxLength": 500,
            "description": "Link: https, http, mailto, tel, a /path or an #anchor; any other scheme is refused with `invalid_url`."
          },
          "banner_2_image": {
            "type": "string",
            "maxLength": 500,
            "description": "Trimmed and cut to 500 characters."
          },
          "hero_slides": {
            "type": "array",
            "items": {},
            "description": "Stored as sent; a non-array value is stored as an empty array."
          },
          "section_order": {
            "type": "array",
            "items": {},
            "description": "Stored as sent; a non-array value is stored as an empty array."
          },
          "show_testimonials": {
            "type": "boolean",
            "description": "Stored as 1 or 0."
          },
          "testimonials_title": {
            "type": "string",
            "maxLength": 200,
            "description": "Plain text, cut to 200 characters."
          },
          "testimonials": {
            "type": "array",
            "maxItems": 24,
            "items": {
              "$ref": "#/components/schemas/StoreTestimonial"
            },
            "description": "At most 24 are kept; entries with neither a name nor a comment are dropped."
          }
        }
      },
      "StoreHomeSectionsUpdateResult": {
        "type": "object",
        "required": [
          "updated",
          "settings"
        ],
        "properties": {
          "updated": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "settings": {
            "$ref": "#/components/schemas/StoreHomeSectionSettings"
          }
        }
      },
      "StoreTestimonial": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 100
          },
          "comment": {
            "type": "string",
            "maxLength": 500
          },
          "stars": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "default": 5,
            "description": "Clamped to 1-5."
          }
        }
      },
      "StoreThemeResult": {
        "type": "object",
        "required": [
          "theme",
          "is_digital"
        ],
        "properties": {
          "theme": {
            "type": "string"
          },
          "is_digital": {
            "type": "boolean",
            "description": "True when the theme is `digital`."
          }
        }
      },
      "StoreThemeSelect": {
        "type": "object",
        "required": [
          "theme"
        ],
        "properties": {
          "theme": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50,
            "pattern": "^[a-zA-Z0-9_-]+$"
          }
        }
      },
      "StoreUpdate": {
        "type": "object",
        "minProperties": 1,
        "additionalProperties": true,
        "description": "Settings-page profile fields. Send only what changes; other keys are ignored. Text is stripped of HTML and cut to the stated length.",
        "properties": {
          "store_name": {
            "type": "string",
            "description": "Cannot be empty. Cut to 100 characters."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 5000 characters."
          },
          "wilaya_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Must be a known wilaya; 0, a negative number or null clears it."
          },
          "commune": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 100 characters."
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 500 characters."
          },
          "store_phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cut to 20 characters."
          },
          "store_email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "description": "Empty clears it. Cut to 255 characters."
          },
          "google_site_verification": {
            "type": [
              "string",
              "null"
            ],
            "description": "Empty clears it. Cut to 100 characters."
          },
          "bing_site_verification": {
            "type": [
              "string",
              "null"
            ],
            "description": "Empty clears it. Cut to 100 characters."
          }
        }
      },
      "StoreUpdateResult": {
        "type": "object",
        "required": [
          "updated",
          "values"
        ],
        "properties": {
          "updated": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Field names written."
          },
          "values": {
            "type": "object",
            "additionalProperties": true,
            "description": "Each written field with the value stored after cleaning."
          }
        }
      },
      "StoreVariantStyleResult": {
        "type": "object",
        "required": [
          "variant_card_style"
        ],
        "properties": {
          "variant_card_style": {
            "type": "string"
          }
        }
      },
      "StoreVariantStyleSelect": {
        "type": "object",
        "required": [
          "style"
        ],
        "properties": {
          "style": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          }
        }
      },
      "Theme": {
        "type": "object",
        "required": [
          "key",
          "title",
          "plan_required",
          "active",
          "color_mode",
          "digital_only",
          "can_use",
          "current"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "description": "Theme name in the store language (Arabic, or French for a French store)."
          },
          "plan_required": {
            "type": "string"
          },
          "active": {
            "type": "boolean",
            "description": "False for a theme that cannot be selected."
          },
          "color_mode": {
            "type": "string",
            "description": "For example `light` (the default) or `dark`."
          },
          "digital_only": {
            "type": "boolean"
          },
          "can_use": {
            "type": "boolean",
            "description": "Whether this store plan may select it."
          },
          "current": {
            "type": "boolean"
          }
        }
      },
      "ThemeList": {
        "type": "object",
        "required": [
          "current",
          "items"
        ],
        "properties": {
          "current": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Theme"
            }
          }
        }
      },
      "UsageCurrent": {
        "type": "object",
        "required": [
          "period",
          "tier",
          "usage",
          "limits"
        ],
        "properties": {
          "period": {
            "type": "string",
            "description": "Current month as YYYY-MM."
          },
          "tier": {
            "type": "string",
            "description": "Rate-limit tier the key resolves to."
          },
          "usage": {
            "type": [
              "object",
              "array"
            ],
            "description": "Map of endpoint group (request, signup, event) to month-to-date totals. Only groups with activity appear; an empty array when there is none.",
            "additionalProperties": {
              "$ref": "#/components/schemas/UsageGroupTotals"
            }
          },
          "limits": {
            "$ref": "#/components/schemas/UsageLimits"
          }
        }
      },
      "UsageGroupTotals": {
        "type": "object",
        "required": [
          "total",
          "billable"
        ],
        "properties": {
          "total": {
            "type": "integer"
          },
          "billable": {
            "type": "integer"
          }
        }
      },
      "UsageHistory": {
        "type": "object",
        "required": [
          "from",
          "to",
          "rows"
        ],
        "properties": {
          "from": {
            "type": "string",
            "description": "Parsed start, ISO 8601 with offset (2026-04-01T00:00:00+01:00), not floored to the hour."
          },
          "to": {
            "type": "string",
            "description": "Parsed end, ISO 8601 with offset, not floored to the hour."
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageHourlyRow"
            },
            "description": "Ascending by period_hour. Hours without usage are absent."
          }
        }
      },
      "UsageHourlyRow": {
        "type": "object",
        "required": [
          "period_hour",
          "endpoint_group",
          "count",
          "billable_count"
        ],
        "properties": {
          "period_hour": {
            "type": "string",
            "description": "Hour bucket as `YYYY-MM-DD HH:00:00`."
          },
          "endpoint_group": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "billable_count": {
            "type": "integer"
          }
        }
      },
      "UsageLimits": {
        "type": "object",
        "required": [
          "requests_per_month",
          "signups_per_month",
          "webhooks_per_month",
          "requests_per_minute"
        ],
        "description": "-1 means unlimited.",
        "properties": {
          "requests_per_month": {
            "type": "integer"
          },
          "signups_per_month": {
            "type": "integer"
          },
          "webhooks_per_month": {
            "type": "integer"
          },
          "requests_per_minute": {
            "type": "integer"
          }
        }
      },
      "Webhook": {
        "type": "object",
        "required": [
          "id",
          "url",
          "events",
          "status",
          "last_success_at",
          "last_failure_at",
          "failure_count",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "dead"
            ]
          },
          "last_success_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Timestamp as `YYYY-MM-DD HH:MM:SS`."
          },
          "last_failure_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "Timestamp as `YYYY-MM-DD HH:MM:SS`."
          },
          "failure_count": {
            "type": "integer",
            "description": "Failed attempts since the last success."
          },
          "created_at": {
            "type": "string",
            "description": "Timestamp as `YYYY-MM-DD HH:MM:SS`."
          }
        }
      },
      "WebhookCreated": {
        "type": "object",
        "required": [
          "id",
          "secret",
          "note"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "secret": {
            "type": "string",
            "description": "64 hex characters, shown only in this response."
          },
          "note": {
            "type": "string"
          }
        }
      },
      "WebhookDeleted": {
        "type": "object",
        "required": [
          "deleted",
          "id"
        ],
        "properties": {
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "id": {
            "type": "integer"
          }
        }
      },
      "WebhookEvent": {
        "type": "string",
        "enum": [
          "order.created",
          "order.confirmed",
          "order.shipped",
          "order.delivered",
          "order.cancelled",
          "order.returned",
          "payment.received",
          "signup.counted",
          "event.recorded",
          "product.stock_low"
        ],
        "description": "Event name. order.* events are sent only for orders created or moved through this API, not for storefront, landing page or dashboard orders. signup.counted is sent for signups counted through a public key. payment.received, event.recorded and product.stock_low are accepted at registration but not sent yet."
      },
      "WebhookInput": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 500,
            "description": "http or https URL on a public hostname, port 80 or 443."
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          }
        }
      },
      "WebhookList": {
        "type": "object",
        "required": [
          "items",
          "allowed_events"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            }
          },
          "allowed_events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          }
        }
      },
      "WebhookTestResult": {
        "type": "object",
        "required": [
          "tested",
          "note"
        ],
        "properties": {
          "tested": {
            "type": "boolean"
          },
          "note": {
            "type": "string"
          }
        }
      },
      "WhatsAppAlreadySent": {
        "type": "object",
        "description": "The 409 of POST /v1/orders/{id}/whatsapp: the template was already sent for this order through the API.",
        "required": [
          "error",
          "meta"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "already_sent"
                ]
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation. Do not parse it."
              },
              "id": {
                "type": "integer",
                "description": "Id of the earlier message, as in GET /v1/whatsapp/messages."
              },
              "status": {
                "type": "string",
                "description": "Status of the earlier message, as in GET /v1/whatsapp/messages."
              }
            },
            "additionalProperties": true
          },
          "meta": {
            "$ref": "#/components/schemas/Meta"
          }
        }
      },
      "WhatsAppBalance": {
        "type": "object",
        "required": [
          "balance",
          "low_balance",
          "addon_active",
          "stats"
        ],
        "properties": {
          "balance": {
            "type": "integer",
            "description": "Messages left in the wallet."
          },
          "low_balance": {
            "type": "boolean",
            "description": "True under 50 messages."
          },
          "addon_active": {
            "type": "boolean",
            "description": "Whether the WhatsApp sender addon is active for the store."
          },
          "stats": {
            "type": "object",
            "required": [
              "sent",
              "delivered",
              "read",
              "failed",
              "free",
              "used_month"
            ],
            "description": "Message counts of the last 30 days by current status, the delivered messages WhatsApp did not bill, and the credits kept since the first of the month.",
            "properties": {
              "sent": {
                "type": "integer",
                "description": "Accepted by WhatsApp, not delivered yet."
              },
              "delivered": {
                "type": "integer",
                "description": "Delivered or read (read is included, so do not add the two)."
              },
              "read": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "free": {
                "type": "integer",
                "description": "Delivered or read messages WhatsApp did not bill; their credit is back in the wallet."
              },
              "used_month": {
                "type": "integer",
                "description": "Credits taken since the first of the month and not returned: charged messages plus the ones still open."
              }
            }
          }
        }
      },
      "WhatsAppMessage": {
        "type": "object",
        "required": [
          "id",
          "order_id",
          "event",
          "source",
          "status",
          "language",
          "template_name",
          "error_title",
          "refunded",
          "billing",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "order_id": {
            "type": "integer"
          },
          "event": {
            "type": "string",
            "description": "For automatic messages the order event (received, confirmed, shipped, delivery_failed, desk_ready); for API messages the template key that was sent."
          },
          "source": {
            "type": "string",
            "enum": [
              "auto",
              "api"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "sending",
              "sent",
              "delivered",
              "read",
              "failed",
              "skipped"
            ]
          },
          "language": {
            "type": "string",
            "enum": [
              "ar",
              "fr"
            ]
          },
          "template_name": {
            "type": "string"
          },
          "error_title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why a message was skipped or failed."
          },
          "refunded": {
            "type": "boolean",
            "description": "True once the credit went back to the wallet: the message failed, expired undelivered, or was delivered without a charge from WhatsApp."
          },
          "billing": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "charged",
              "free",
              null
            ],
            "description": "null while WhatsApp has not reported the outcome; charged when WhatsApp billed the message; free when it did not (failed, undelivered after 30 days, or delivered without a charge) and the credit is back in the wallet."
          },
          "created_at": {
            "type": "string",
            "description": "YYYY-MM-DD HH:MM:SS, server time."
          }
        }
      },
      "WhatsAppMessageList": {
        "type": "object",
        "required": [
          "items",
          "next_cursor",
          "has_more"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WhatsAppMessage"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "WhatsAppSendInput": {
        "type": "object",
        "required": [
          "template"
        ],
        "properties": {
          "template": {
            "type": "string",
            "enum": [
              "shipped",
              "received",
              "confirmed",
              "shipped_home",
              "shipped_desk",
              "delivery_failed",
              "desk_ready"
            ],
            "description": "Template key; shipped picks the variant that matches the order's delivery type."
          },
          "language": {
            "type": "string",
            "enum": [
              "ar",
              "fr"
            ],
            "description": "Defaults to the language set in the addon."
          }
        }
      },
      "WhatsAppSendQueued": {
        "type": "object",
        "required": [
          "message_id",
          "status",
          "template",
          "language"
        ],
        "properties": {
          "message_id": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "const": "queued"
          },
          "template": {
            "type": "string",
            "enum": [
              "received",
              "confirmed",
              "shipped_home",
              "shipped_desk",
              "delivery_failed",
              "desk_ready"
            ],
            "description": "Template key that was queued (shipped resolved to its variant)."
          },
          "language": {
            "type": "string",
            "enum": [
              "ar",
              "fr"
            ]
          }
        }
      },
      "WhatsAppTemplate": {
        "type": "object",
        "required": [
          "key",
          "name",
          "toggle",
          "languages"
        ],
        "properties": {
          "key": {
            "type": "string",
            "enum": [
              "received",
              "confirmed",
              "shipped_home",
              "shipped_desk",
              "delivery_failed",
              "desk_ready"
            ]
          },
          "name": {
            "type": "string",
            "description": "Template name registered with WhatsApp."
          },
          "toggle": {
            "type": "string",
            "enum": [
              "received",
              "confirmed",
              "shipped",
              "delivery_failed",
              "desk_ready"
            ],
            "description": "Automatic-message switch in the addon settings that owns this template."
          },
          "languages": {
            "type": "object",
            "required": [
              "ar",
              "fr"
            ],
            "properties": {
              "ar": {
                "$ref": "#/components/schemas/WhatsAppTemplateLanguage"
              },
              "fr": {
                "$ref": "#/components/schemas/WhatsAppTemplateLanguage"
              }
            }
          }
        }
      },
      "WhatsAppTemplateLanguage": {
        "type": "object",
        "required": [
          "body",
          "example",
          "status"
        ],
        "properties": {
          "body": {
            "type": "string",
            "description": "Message text with {{n}} placeholders."
          },
          "example": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Example values for the placeholders, in order."
          },
          "status": {
            "type": "string",
            "description": "Approval status last read from WhatsApp (APPROVED, PENDING, REJECTED, PAUSED ...), or UNKNOWN when not read yet. Only APPROVED templates are sent."
          }
        }
      },
      "WhatsAppTemplateList": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WhatsAppTemplate"
            }
          }
        }
      },
      "WhoAmI": {
        "type": "object",
        "required": [
          "key_id",
          "store_id",
          "type",
          "rate_limit_tier",
          "pilot",
          "scopes"
        ],
        "properties": {
          "key_id": {
            "type": "string"
          },
          "store_id": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "enum": [
              "platform",
              "public"
            ]
          },
          "rate_limit_tier": {
            "type": "string",
            "description": "Tier in force, following the store current plan."
          },
          "pilot": {
            "type": "boolean"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "app": {
            "type": "object",
            "description": "Present only when the key belongs to an installed app.",
            "required": [
              "app_id",
              "client_id",
              "install_id"
            ],
            "properties": {
              "app_id": {
                "type": "integer"
              },
              "client_id": {
                "type": "string"
              },
              "install_id": {
                "type": "integer"
              }
            }
          }
        }
      },
      "Wilaya": {
        "type": "object",
        "required": [
          "id",
          "name_ar",
          "name_fr",
          "name_en"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name_ar": {
            "type": [
              "string",
              "null"
            ]
          },
          "name_fr": {
            "type": [
              "string",
              "null"
            ]
          },
          "name_en": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "WilayaList": {
        "type": "object",
        "required": [
          "wilaya_mode",
          "mode_note",
          "count",
          "wilayas"
        ],
        "properties": {
          "wilaya_mode": {
            "type": "string",
            "description": "`58` (courier-compatible) or `69`."
          },
          "mode_note": {
            "type": "string",
            "description": "One English sentence explaining the mode."
          },
          "count": {
            "type": "integer"
          },
          "wilayas": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Wilaya"
            }
          }
        }
      }
    },
    "parameters": {
      "cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Opaque cursor from the previous page's next_cursor. Omit it for the first page. An unreadable cursor is ignored and the first page is returned.",
        "schema": {
          "type": "string"
        }
      },
      "idempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Unique key for this write: 1 to 64 letters, digits, _, -, : or . characters. A retry with the same key and the same body replays the stored answer for 24 hours; the same key with a different body is refused with 422 idempotency_key_reuse.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64,
          "pattern": "^[A-Za-z0-9_:.-]+$"
        }
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Page size, default 50. Values below 1 become 1 and values above 200 become 200.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "xRequestId": {
        "name": "X-Request-Id",
        "in": "header",
        "required": false,
        "description": "Your own id for this request. It is echoed back in meta.request_id.",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "IdempotencyReplay": {
        "description": "\"1\" when this response was replayed from an earlier request with the same Idempotency-Key.",
        "schema": {
          "type": "string",
          "enum": [
            "1"
          ]
        }
      },
      "XRequestId": {
        "description": "The X-Request-Id you sent, or an id generated for this request; also in meta.request_id.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request is malformed: invalid JSON, a missing or invalid field, or a missing Idempotency-Key. error.code is bad_request unless the operation names another code.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "The request conflicts with the current state of the resource. The operation description names the error.code values.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key or token is valid but may not do this: a missing scope, a store outside the pilot or without an active Enterprise plan, or an operation-specific refusal named in its description.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "The resource does not exist in this store. error.code is not_found unless the operation names another code.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "The request body is larger than 1 MB. error.code is payload_too_large.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "QuotaExceeded": {
        "description": "error.code is quota_exceeded: the store has used its monthly request allowance, or, on AI generation, the store does not have enough AI credits, or, on POST /v1/orders/{id}/whatsapp, error.code is no_credit: the store's WhatsApp wallet is empty.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests. error.code is rate_limited when the per-minute budget for the store (or for this endpoint) is spent, or too_many_concurrent when too many calls of this kind are already running for the store; some endpoints add their own code, named in the operation description. Wait for Retry-After seconds.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          },
          "Retry-After": {
            "description": "Seconds to wait before the next attempt.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServerError": {
        "description": "Unexpected error on our side. error.code is server_error. Retry with backoff and quote meta.request_id if it persists.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "A dependency this operation needs is unavailable, or the store is not ready for this change yet. Nothing was changed; retry later. The operation description names the error.code values.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The API key or access token is missing, malformed, revoked or expired. error.code is unauthorized.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unprocessable": {
        "description": "The request is well formed but cannot be applied. The operation description names the error.code values, including idempotency_key_reuse on writes.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/XRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "dzOAuth": {
        "type": "oauth2",
        "description": "OAuth 2.0 authorization code flow with PKCE (S256) for apps a merchant installs from the DZBuild dashboard. The merchant approves the app on a consent screen and picks the stores; the token endpoint answers with one install token per store. Send it as Authorization: Bearer <token>. Install tokens have no expiry and no refresh token; a token stops working when the merchant uninstalls the app or installs it again on the same store.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://dzbuild.com/oauth/apps/authorize",
            "tokenUrl": "https://dzbuild.com/oauth/apps/token",
            "scopes": {
              "analytics:read": "Read store analytics and KPIs.",
              "customers:read": "Read customers and each customer's orders.",
              "delivery:send": "Hand orders to the store's courier after the merchant confirms. Not included in merchant keys; granted to assistant connections.",
              "landing_pages:read": "Read landing pages, their sections, publish checks and AI generation status.",
              "landing_pages:write": "Create, update, publish and delete landing pages and their sections.",
              "orders:read": "Read orders, their items and courier shipment state.",
              "orders:write": "Create orders, change their status and cancel them.",
              "pixels:read": "Read tracking pixels (access tokens are never returned).",
              "pixels:write": "Add, update and delete tracking pixels.",
              "products:read": "Read products and categories, with each product's images, variants, input fields, offers, quantity rules and stock.",
              "products:write": "Create, update and delete products and categories, with each product's images, variants, input fields, offers, quantity rules and stock.",
              "promos:read": "Read promo codes.",
              "promos:write": "Create, update and delete promo codes.",
              "shipping:read": "Read shipping rates and settings, linked couriers, courier coverage and the wilaya and commune lists.",
              "shipping:write": "Change shipping rates and settings, and link, test, unlink or sync couriers.",
              "store:read": "Read the store profile, design, home page sections and themes, the assistant connection and the configuration change history.",
              "store:write": "Update the store profile, design, home page sections and theme, and switch the active store of an assistant connection.",
              "whatsapp:read": "Read the WhatsApp order message templates, the WhatsApp wallet balance and the message log. Included in merchant keys created from v1.6 on; an older key needs a new key.",
              "whatsapp:send": "Send WhatsApp order messages to buyers, each paid from the store's WhatsApp wallet. Included in merchant keys created from v1.6 on; an older key needs a new key."
            }
          }
        }
      }
    }
  }
}
