{
  "openapi": "3.1.0",
  "info": {
    "title": "QRissimo API",
    "version": "1.0.0",
    "description": "API access through a verified Business subscription, a secured owner account or active owner-granted rights. Granular permissions; legacy qr:* keys remain compatible without receiving new scopes. Each key is bound to its personal/company workspace. 60 requests/minute/payer and 120/minute/IP. Company keys require current owner/admin membership and 2FA. Retry-After on 429. Batch access is separately checked. No user account password or payment administration through API; shared visitor passwords require explicit access scopes. PNG/JPEG support complete approved compositions or QR-only output; stateless images do not store destinations. Restaurant menus and requests have seven independent scopes; no legacy QR inheritance. Menu publication and manual confirmation are distinct. No meal payments."
  },
  "servers": [
    {
      "url": "https://qrissimo.fr/api/v1"
    }
  ],
  "paths": {
    "/qr-codes": {
      "get": {
        "summary": "List codes, 20 per page",
        "description": "Requires codes:read.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated codes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/QrCode"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "current_page": {
                          "type": "integer"
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Idempotency conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Creation was trashed."
          },
          "422": {
            "description": "Invalid input, contrast or quota."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_qr_codes"
      },
      "post": {
        "summary": "Create a static or dynamic code idempotently",
        "description": "Requires codes:create. Without codes:read, only the operation-specific confirmation is returned, excluding private content. Legacy qr:write mutation responses remain compatible. Creating content_type=page (and replaying the same creation) additionally requires pages:write; page_type is required and kind must be dynamic. One reusable QR slot, private empty draft, response page_id. Current page type is immutable.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Existing identical creation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/QrCode"
                        },
                        {
                          "$ref": "#/components/schemas/QrCreatedReference"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/QrCode"
                        },
                        {
                          "$ref": "#/components/schemas/QrCreatedReference"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Idempotency conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Creation was trashed."
          },
          "422": {
            "description": "Invalid input, contrast or quota."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateQrCode"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._:-]{1,128}$"
            }
          }
        ],
        "operationId": "post_qr_codes"
      }
    },
    "/qr-codes/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "summary": "Read a code",
        "description": "Requires codes:read.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Code",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/QrCode"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Idempotency conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Creation was trashed."
          },
          "422": {
            "description": "Invalid input, contrast or quota."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_qr_codes_id"
      },
      "patch": {
        "summary": "Change destination or disabled state",
        "description": "Requires codes:update. Changing disabled also requires codes:state. Without codes:read, only the operation-specific confirmation is returned, excluding private content. Legacy qr:write mutation responses remain compatible.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/QrCode"
                        },
                        {
                          "$ref": "#/components/schemas/QrUpdateResult"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Idempotency conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Creation was trashed."
          },
          "422": {
            "description": "Invalid input, contrast or quota."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "destination": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 1000
                  },
                  "disabled": {
                    "type": "boolean"
                  },
                  "campaign_id": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1
                  },
                  "starts_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$",
                    "description": "Local clock time in timezone, minute precision. null clears the boundary. Nonexistent or repeated DST times are rejected."
                  },
                  "ends_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$",
                    "description": "Local clock time in timezone, minute precision. null clears the boundary. Nonexistent or repeated DST times are rejected."
                  },
                  "timezone": {
                    "type": "string",
                    "enum": [
                      "Europe/Paris",
                      "Europe/London",
                      "Europe/Berlin",
                      "Europe/Rome",
                      "Europe/Madrid",
                      "Europe/Lisbon",
                      "Europe/Bucharest",
                      "Europe/Moscow",
                      "Europe/Kyiv",
                      "Europe/Amsterdam",
                      "Europe/Warsaw",
                      "Europe/Prague",
                      "Europe/Bratislava",
                      "Europe/Budapest",
                      "Europe/Athens",
                      "Europe/Sofia",
                      "Europe/Zagreb",
                      "Europe/Belgrade",
                      "Europe/Istanbul",
                      "Europe/Stockholm",
                      "Europe/Copenhagen",
                      "Europe/Helsinki",
                      "Europe/Oslo",
                      "UTC",
                      "America/New_York",
                      "America/Los_Angeles",
                      "America/Sao_Paulo"
                    ]
                  },
                  "fallback_url": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "maxLength": 1000,
                    "description": "HTTP(S) only. Requires ends_at. Used after expiry only, never for disabled codes, unpaid subscriptions or suspended accounts."
                  },
                  "content_type": {
                    "type": "string",
                    "enum": [
                      "url",
                      "text",
                      "wifi",
                      "vcard",
                      "email",
                      "phone",
                      "sms",
                      "whatsapp"
                    ],
                    "default": "url"
                  },
                  "text": {
                    "type": "string",
                    "maxLength": 1200
                  },
                  "wifi_ssid": {
                    "type": "string",
                    "maxLength": 32,
                    "description": "Required for wifi. At most 32 UTF-8 bytes. Whitespace is preserved."
                  },
                  "wifi_password": {
                    "type": "string",
                    "maxLength": 64,
                    "description": "Required for WPA: 8–63 bytes, or 64 hexadecimal characters. Blank on PATCH retains the current secret. Encrypted at rest and never returned in JSON. A static WIFI QR necessarily contains the password: share guest networks only."
                  },
                  "contact_first": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "contact_last": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "contact_org": {
                    "type": "string",
                    "maxLength": 150
                  },
                  "contact_title": {
                    "type": "string",
                    "maxLength": 150
                  },
                  "contact_phone": {
                    "type": "string",
                    "maxLength": 40,
                    "description": "International + followed by 6–15 digits. Spaces, parentheses, dots and dashes are removed."
                  },
                  "contact_email": {
                    "type": "string",
                    "maxLength": 254,
                    "format": "email"
                  },
                  "contact_url": {
                    "type": "string",
                    "maxLength": 500,
                    "format": "uri"
                  },
                  "contact_street": {
                    "type": "string",
                    "maxLength": 150
                  },
                  "contact_city": {
                    "type": "string",
                    "maxLength": 150
                  },
                  "contact_zip": {
                    "type": "string",
                    "maxLength": 150
                  },
                  "contact_country": {
                    "type": "string",
                    "maxLength": 150
                  },
                  "email_to": {
                    "type": "string",
                    "maxLength": 254,
                    "format": "email"
                  },
                  "email_subject": {
                    "type": "string",
                    "maxLength": 150
                  },
                  "phone": {
                    "type": "string",
                    "maxLength": 40,
                    "description": "International + followed by 6–15 digits. Spaces, parentheses, dots and dashes are removed."
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Optional email/SMS/WhatsApp body. Unicode and whitespace preserved."
                  },
                  "wifi_security": {
                    "type": "string",
                    "enum": [
                      "WPA",
                      "nopass"
                    ]
                  },
                  "wifi_hidden": {
                    "type": "boolean",
                    "default": false
                  }
                },
                "description": "Omitted fields stay unchanged. Dates are local clock times. Changing timezone alone preserves existing instants; supplying a date reinterprets that value in the supplied timezone. The printed QR remains unchanged. Content fields may be patched for the existing type. Omitted fields are preserved. Type changes are rejected. Static codes cannot be patched."
              }
            }
          }
        },
        "operationId": "patch_qr_codes_id"
      },
      "delete": {
        "summary": "Disable without freeing a slot",
        "description": "Requires codes:state.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "204": {
            "description": "Disabled"
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Idempotency conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Creation was trashed."
          },
          "422": {
            "description": "Invalid input, contrast or quota."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "operationId": "delete_qr_codes_id"
      }
    },
    "/qr-codes/{id}/svg": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "summary": "Export QR SVG or full composition, counts a download",
        "description": "Requires codes:export. By default returns only the QR matrix. Pass composition=1 for the saved silhouette, caption and arrow.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "SVG image",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Idempotency conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Creation was trashed."
          },
          "422": {
            "description": "Invalid input, contrast or quota."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "parameters": [
          {
            "name": "composition",
            "in": "query",
            "required": false,
            "description": "0: QR only (default); 1: full saved composition.",
            "schema": {
              "type": "integer",
              "enum": [
                0,
                1
              ],
              "default": 0
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_qr_codes_id_svg"
      }
    },
    "/qr-codes/{id}/statistics": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "summary": "Link openings and downloads, not unique visitors",
        "description": "Requires statistics:read. Accepts period filters. code_id is forced to this owned code. Existing lifetime counts stay unchanged; data.report adds the period report. This endpoint does not expose trashed codes; global statistics include their history.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Counters",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "openings": {
                          "type": "integer"
                        },
                        "downloads": {
                          "type": "integer"
                        },
                        "openings_measured": {
                          "type": "boolean"
                        },
                        "report": {
                          "$ref": "#/components/schemas/StatisticsReport"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Idempotency conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Creation was trashed."
          },
          "422": {
            "description": "Invalid input, contrast or quota."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "timezone",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "Europe/Paris",
                "Europe/London",
                "Europe/Berlin",
                "Europe/Rome",
                "Europe/Madrid",
                "Europe/Lisbon",
                "Europe/Bucharest",
                "Europe/Moscow",
                "Europe/Kyiv",
                "Europe/Amsterdam",
                "Europe/Warsaw",
                "Europe/Prague",
                "Europe/Bratislava",
                "Europe/Budapest",
                "Europe/Athens",
                "Europe/Sofia",
                "Europe/Zagreb",
                "Europe/Belgrade",
                "Europe/Istanbul",
                "Europe/Stockholm",
                "Europe/Copenhagen",
                "Europe/Helsinki",
                "Europe/Oslo",
                "UTC",
                "America/New_York",
                "America/Los_Angeles",
                "America/Sao_Paulo"
              ],
              "default": "Europe/Paris"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "code_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_qr_codes_id_statistics"
      }
    },
    "/statistics": {
      "get": {
        "summary": "Period statistics, daily series and paginated code comparison",
        "description": "Requires statistics:read. Defaults to the last 30 calendar days. Inclusive local dates, at most 366 days. UTC timestamps. Includes trash history. Static codes have no measurable openings. Repeated and automated visits may be counted; these are not unique visitors. CSV/statistics requests do not increase QR download counts.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StatisticsReport"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid data or period."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "timezone",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "Europe/Paris",
                "Europe/London",
                "Europe/Berlin",
                "Europe/Rome",
                "Europe/Madrid",
                "Europe/Lisbon",
                "Europe/Bucharest",
                "Europe/Moscow",
                "Europe/Kyiv",
                "Europe/Amsterdam",
                "Europe/Warsaw",
                "Europe/Prague",
                "Europe/Bratislava",
                "Europe/Budapest",
                "Europe/Athens",
                "Europe/Sofia",
                "Europe/Zagreb",
                "Europe/Belgrade",
                "Europe/Istanbul",
                "Europe/Stockholm",
                "Europe/Copenhagen",
                "Europe/Helsinki",
                "Europe/Oslo",
                "UTC",
                "America/New_York",
                "America/Los_Angeles",
                "America/Sao_Paulo"
              ],
              "default": "Europe/Paris"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "code_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_statistics"
      }
    },
    "/statistics.csv": {
      "get": {
        "summary": "Export period statistics as CSV",
        "description": "Requires statistics:read. Complete export limited to 5,000 QR codes and 4 MiB; filter by campaign_id, code_id or q for a larger collection. Preparation finishes before authorization is rechecked; no partial file is returned.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "UTF-8 CSV with BOM, semicolon delimiters, CRLF, formula-safe cells and English column headers. One row per QR. Static openings are marked unmeasured. No pagination.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid data or period, more than 5,000 selected QR codes, or CSV larger than 4 MiB. Refused as a whole; no truncation."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "timezone",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "Europe/Paris",
                "Europe/London",
                "Europe/Berlin",
                "Europe/Rome",
                "Europe/Madrid",
                "Europe/Lisbon",
                "Europe/Bucharest",
                "Europe/Moscow",
                "Europe/Kyiv",
                "Europe/Amsterdam",
                "Europe/Warsaw",
                "Europe/Prague",
                "Europe/Bratislava",
                "Europe/Budapest",
                "Europe/Athens",
                "Europe/Sofia",
                "Europe/Zagreb",
                "Europe/Belgrade",
                "Europe/Istanbul",
                "Europe/Stockholm",
                "Europe/Copenhagen",
                "Europe/Helsinki",
                "Europe/Oslo",
                "UTC",
                "America/New_York",
                "America/Los_Angeles",
                "America/Sao_Paulo"
              ],
              "default": "Europe/Paris"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "code_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_statistics_csv"
      }
    },
    "/campaigns": {
      "get": {
        "summary": "List campaigns, 20 per page",
        "description": "Requires campaigns:read.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "data",
                        "current_page",
                        "last_page",
                        "total"
                      ],
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Campaign"
                          }
                        },
                        "current_page": {
                          "type": "integer"
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid data or period."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_campaigns"
      },
      "post": {
        "summary": "Create a campaign",
        "description": "Requires campaigns:write.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Campaign"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid data or period."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "operationId": "post_campaigns"
      }
    },
    "/campaigns/{campaign}": {
      "parameters": [
        {
          "name": "campaign",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer",
            "minimum": 1
          }
        }
      ],
      "get": {
        "summary": "Read a campaign and its active library codes, 20 per page",
        "description": "Requires campaigns:read. QR entries are summaries unless the key also has codes:read; private contents are not revealed by campaigns:read alone.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Campaign"
                    },
                    "codes": {
                      "type": "object",
                      "required": [
                        "data",
                        "current_page",
                        "last_page",
                        "total"
                      ],
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "anyOf": [
                              {
                                "$ref": "#/components/schemas/QrCode"
                              },
                              {
                                "$ref": "#/components/schemas/QrSummary"
                              }
                            ]
                          }
                        },
                        "current_page": {
                          "type": "integer"
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid data or period."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_campaigns_campaign"
      },
      "patch": {
        "summary": "Rename a campaign",
        "description": "Requires campaigns:write.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Campaign"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid data or period."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "operationId": "patch_campaigns_campaign"
      },
      "delete": {
        "summary": "Delete grouping, keeping QR codes, links and statistics",
        "description": "Requires campaigns:write.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "204": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid data or period."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "operationId": "delete_campaigns_campaign"
      }
    },
    "/campaigns/{campaign}/codes": {
      "parameters": [
        {
          "name": "campaign",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer",
            "minimum": 1
          }
        }
      ],
      "post": {
        "summary": "Add or remove an atomic selection",
        "description": "Requires campaigns:write. One campaign per QR. Adding moves a code from its previous campaign. Removing only affects codes in this campaign. Any missing, trashed or foreign ID fails the entire selection. updated reports affected rows.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "updated": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid data or period."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "codes",
                  "action"
                ],
                "properties": {
                  "codes": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "uniqueItems": true,
                    "items": {
                      "type": "integer",
                      "minimum": 1
                    }
                  },
                  "action": {
                    "enum": [
                      "add",
                      "remove"
                    ]
                  }
                }
              }
            }
          }
        },
        "operationId": "post_campaigns_campaign_codes"
      }
    },
    "/qr-codes/{id}/appearance": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "patch": {
        "summary": "Customize a static or dynamic QR code",
        "description": "Requires codes:style (legacy qr:write accepted). Only provided fields are changed. Content, payload, short URL and schedule remain identical. Contrast is checked. SVG/HTML and quiet-zone changes are not accepted. Without codes:read, only the operation-specific confirmation is returned, excluding private content. Legacy qr:write mutation responses remain compatible.",
        "security": [
          {
            "bearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QrAppearance"
              },
              "example": {
                "template": "vinyl",
                "caption": "Scannez-moi !",
                "arrow": "curve"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/QrCode"
                        },
                        {
                          "$ref": "#/components/schemas/QrAppearanceResult"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Idempotency conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Creation was trashed."
          },
          "422": {
            "description": "Invalid input, contrast or quota."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "operationId": "patch_qr_codes_id_appearance"
      }
    },
    "/catalog": {
      "get": {
        "summary": "Get all personalization options",
        "description": "Requires catalog:read (legacy qr:read accepted). Localized names, 60 templates, eight content types, module/finder styles and arrows. English by default. Includes 24 business profiles with 3 goals each: identifiers, localized names and hints, existing content_type and suggested template. These are creation guides for existing URLs or native vCard/Wi-Fi, not hosted websites or a grant of dynamic/tracking rights. Locale is one of the 7 active catalogue languages.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "it",
                "es",
                "de",
                "pt",
                "ro",
                "ru",
                "uk",
                "nl",
                "pl",
                "cs",
                "sk",
                "hu",
                "el",
                "bg",
                "hr",
                "sr",
                "tr",
                "sv",
                "da",
                "fi",
                "nb"
              ],
              "default": "en"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "responses": {
          "200": {
            "description": "Catalog",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "locale",
                        "locales",
                        "templates",
                        "content_types",
                        "modules",
                        "finders",
                        "arrows",
                        "quiet_zone_min_modules",
                        "caption_max_characters"
                      ],
                      "properties": {
                        "locale": {
                          "type": "string"
                        },
                        "locales": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string"
                          }
                        },
                        "templates": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "id",
                              "name",
                              "theme",
                              "orientation"
                            ],
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              },
                              "theme": {
                                "type": "string"
                              },
                              "orientation": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "content_types": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "id",
                              "name",
                              "kinds"
                            ],
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              },
                              "kinds": {
                                "type": "array",
                                "items": {
                                  "type": "string",
                                  "enum": [
                                    "static",
                                    "dynamic"
                                  ]
                                },
                                "description": "Raw imports support static only; other types support both, subject to account entitlements."
                              }
                            }
                          }
                        },
                        "modules": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "finders": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "arrows": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "quiet_zone_min_modules": {
                          "type": "integer",
                          "minimum": 4
                        },
                        "caption_max_characters": {
                          "type": "integer",
                          "const": 30
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Idempotency conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Creation was trashed."
          },
          "422": {
            "description": "Invalid input, contrast or quota."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "operationId": "get_catalog"
      }
    },
    "/qr-codes/{id}/state": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "patch": {
        "summary": "Disable or reactivate a dynamic link",
        "description": "Requires codes:state (legacy qr:write accepted). Only the activation state changes; contents, matrix and short URL stay identical. Static QR codes are not affected. Without codes:read, only the operation-specific confirmation is returned, excluding private content. Legacy qr:write mutation responses remain compatible.",
        "security": [
          {
            "bearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "disabled"
                ],
                "properties": {
                  "disabled": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "disabled": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/QrCode"
                        },
                        {
                          "$ref": "#/components/schemas/QrStateResult"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing entitlement or scope."
          },
          "404": {
            "description": "Not found or not owned."
          },
          "409": {
            "description": "Idempotency conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Creation was trashed."
          },
          "422": {
            "description": "Invalid input, contrast or quota."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "operationId": "patch_qr_codes_id_state"
      }
    },
    "/print-sheets": {
      "post": {
        "summary": "Generate an A4 PDF print sheet",
        "description": "Requires prints:export, with no automatic legacy-scope inheritance. Up to 48 non-trashed codes in the key workspace, sorted by ascending ID. 2/6/12/24 codes per page, optional labels, persisted appearance. Density must allow at least 0.3 mm/module. Each exported QR records one API/PDF download after successful rendering and a fresh access check. This is an export, not an idempotent mutation: each successful retry increments downloads. Limit 5/minute/actor, 10/minute/payer and 20/minute/IP in addition to global API limits. Error bodies are JSON even with Accept: application/pdf.",
        "security": [
          {
            "bearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "codes",
                  "layout"
                ],
                "properties": {
                  "codes": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 48,
                    "uniqueItems": true,
                    "items": {
                      "type": "integer",
                      "minimum": 1
                    }
                  },
                  "layout": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "enum": [
                          2,
                          6,
                          12,
                          24
                        ]
                      },
                      {
                        "type": "string",
                        "const": "custom"
                      }
                    ]
                  },
                  "labels": {
                    "type": "boolean",
                    "default": false
                  },
                  "print": {
                    "$ref": "#/components/schemas/PrintGeometry"
                  },
                  "brand_id": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1,
                    "description": "Optional active style kit in the same workspace; requires kits:read in addition to prints:export. Applies the label identity, never replaces the saved QR drawing."
                  }
                },
                "examples": [
                  {
                    "codes": [
                      42,
                      43
                    ],
                    "layout": 12,
                    "labels": true
                  },
                  {
                    "codes": [
                      42,
                      43
                    ],
                    "layout": "custom",
                    "labels": true,
                    "brand_id": 5,
                    "print": {
                      "orientation": "portrait",
                      "width": 60,
                      "height": 60,
                      "gap_x": 5,
                      "gap_y": 5,
                      "copies": 4,
                      "skip": 2,
                      "crop_marks": true
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A4 PDF, private and not cached.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing scope, subscription or current workspace access."
          },
          "404": {
            "description": "At least one code is absent, trashed or outside the key workspace."
          },
          "409": {
            "description": "Conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Document/input size limit; no file or download count."
          },
          "422": {
            "description": "Invalid selection/layout, unsupported field, or too dense to print."
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "503": {
            "description": "Bounded PDF worker unavailable; no file or download count."
          }
        },
        "operationId": "post_print_sheets"
      }
    },
    "/qr-codes/{id}/png": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "summary": "Export a QR-only PNG image",
        "description": "Requires codes:export (legacy qr:export supported). No decoration, caption or arrow; use SVG composition=1 or PDF for full artwork. Opaque saved background and four-module minimum quiet zone. size is the only accepted query parameter. 10/minute/actor, 20/minute/subscription owner, 30/minute/IP; limits shared with stateless images. Current authority is rechecked after rendering.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "size",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                512,
                1024,
                2048
              ],
              "default": 1024
            },
            "description": "Longest image side in pixels. Dense codes are refused when final QR modules would be smaller than four pixels; use a larger size/template or SVG."
          },
          {
            "name": "composition",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Use 1 to preserve the stored template, caption and arrow. Default exports the QR only."
          },
          {
            "name": "transparent",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Use 1 only with composition=1. Unpainted canvas is transparent; QR background and four-module quiet zone remain opaque. JPEG always uses an opaque background."
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "responses": {
          "200": {
            "description": "Private QR-only bitmap by default, or complete approved design with composition=1. Longest side equals size; original aspect ratio preserved. One recorded download after success and current access checks.",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing permission, entitlement or current workspace access."
          },
          "404": {
            "description": "Not found, trashed or outside the workspace."
          },
          "409": {
            "description": "QR changed during generation. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid fields, size, density or contrast."
          },
          "429": {
            "description": "Rate limit; respect Retry-After."
          },
          "503": {
            "description": "GD image generation unavailable on this server."
          }
        },
        "operationId": "get_qr_codes_id_png"
      }
    },
    "/qr-codes/{id}/jpeg": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "summary": "Export a QR-only JPEG image",
        "description": "Requires codes:export (legacy qr:export supported). No decoration, caption or arrow; use SVG composition=1 or PDF for full artwork. Opaque saved background and four-module minimum quiet zone. size is the only accepted query parameter. 10/minute/actor, 20/minute/subscription owner, 30/minute/IP; limits shared with stateless images. Current authority is rechecked after rendering.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "size",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                512,
                1024,
                2048
              ],
              "default": 1024
            },
            "description": "Longest image side in pixels. Dense codes are refused when final QR modules would be smaller than four pixels; use a larger size/template or SVG."
          },
          {
            "name": "composition",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Use 1 to preserve the stored template, caption and arrow. Default exports the QR only."
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "responses": {
          "200": {
            "description": "Private QR-only bitmap by default, or complete approved design with composition=1. Longest side equals size; original aspect ratio preserved. One recorded download after success and current access checks.",
            "content": {
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing permission, entitlement or current workspace access."
          },
          "404": {
            "description": "Not found, trashed or outside the workspace."
          },
          "409": {
            "description": "QR changed during generation. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid fields, size, density or contrast."
          },
          "429": {
            "description": "Rate limit; respect Retry-After."
          },
          "503": {
            "description": "GD image generation unavailable on this server."
          }
        },
        "operationId": "get_qr_codes_id_jpeg"
      }
    },
    "/qr-images": {
      "post": {
        "summary": "Render a QR image for an email without creating a library QR",
        "description": "Requires images:render explicitly; not inherited from qr:export or codes:create. HTTPS destination is encoded without being fetched or stored. Only actor, payer, workspace, key, format, size and time are recorded. No editable redirect or scan statistics. Same safety limits as bitmap exports. Embed bytes as a CID attachment in a server-side mailer; never expose the API token to the browser. Errors are JSON even when Accept requests an image. composition=true supports all sixty approved templates, a caption up to 30 Unicode characters and the three arrow designs. transparent=true only works with complete PNG; QR background and quiet zone stay opaque. Template, caption and destination are never persisted. Undersized dense codes are refused with 422.",
        "security": [
          {
            "bearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QrImage"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "PNG by default; JPEG when requested. Private, no-store.",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing permission, entitlement or current workspace access."
          },
          "404": {
            "description": "Not found, trashed or outside the workspace."
          },
          "409": {
            "description": "QR changed during generation. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid fields, size, density or contrast."
          },
          "429": {
            "description": "Rate limit; respect Retry-After."
          },
          "503": {
            "description": "GD image generation unavailable on this server."
          }
        },
        "operationId": "post_qr_images"
      }
    },
    "/batches": {
      "get": {
        "summary": "List current batches",
        "description": "Requires batches:read and active batch/API entitlement. Pending drafts are visible for 24 hours; completed results remain. Summaries only. No raw source cells.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated summaries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Batch"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "current_page": {
                          "type": "integer"
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "per_page": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, batch/API rights, or required membership/2FA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource outside key workspace or not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/idempotency/content/access conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Draft expired or discarded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, file, row structure, quota or explicit exclusions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_batches"
      },
      "post": {
        "summary": "Import a batch for review",
        "description": "Requires batches:write and active batch/API access. No QR created yet. CSV/XLSX or JSON rows, maximum 100 data rows, 32 columns, 1 MB and five pending drafts/workspace. Default static QR. JSON creates review immediately; files need mapping. Row errors never silently excluded. Five uploads/minute/actor, ten/minute/payer, twenty/minute/IP, shared web/API. Unknown fields rejected.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Idempotent replay",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Batch"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "New draft",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Batch"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, batch/API rights, or required membership/2FA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource outside key workspace or not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/idempotency/content/access conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Draft expired or discarded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, file, row structure, quota or explicit exclusions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9_.:-]{1,128}$"
            },
            "description": "Scoped to payer and workspace, shared across keys. Same normalized input returns the same batch (200); different input 409. Expired/cancelled drafts 410. No duplicate QR creation on upload retry."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchJsonInput"
              },
              "example": {
                "kind": "static",
                "template": "vinyl",
                "caption": "Scannez-moi",
                "arrow": "curve",
                "rows": [
                  {
                    "label": "Menu",
                    "destination": "https://example.test/menu",
                    "reference": "00042"
                  }
                ]
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/BatchFileInput"
              }
            }
          }
        },
        "operationId": "post_batches"
      }
    },
    "/batches/{batch}": {
      "get": {
        "summary": "Read batch review/results",
        "description": "Requires batches:read. Scoped normalized rows, machine errors and created IDs; no original workbook or unselected cells.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Batch",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Batch"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, batch/API rights, or required membership/2FA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource outside key workspace or not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/idempotency/content/access conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Draft expired or discarded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, file, row structure, quota or explicit exclusions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "batch",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_batches_batch"
      },
      "delete": {
        "summary": "Discard a pending draft",
        "description": "Requires batches:write. Clears encrypted prepared contents. Completed batches cannot be cancelled. Repeated access to expired/discarded draft is 410.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "204": {
            "description": "Discarded; no body"
          },
          "401": {
            "description": "Invalid, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, batch/API rights, or required membership/2FA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource outside key workspace or not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/idempotency/content/access conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Draft expired or discarded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, file, row structure, quota or explicit exclusions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "batch",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "operationId": "delete_batches_batch"
      }
    },
    "/batches/{batch}/mapping": {
      "patch": {
        "summary": "Map columns and apply batch appearance",
        "description": "Requires batches:write. Zero-based column indices. All mapping fields other than name/destination optional; omitted optional fields are cleared. revision=null on first mapping, latest revision afterwards. A stale mapping/confirmation is rejected. Resets defaults for unspecified style fields; submit desired full batch appearance. Ten prepares/minute/actor and payer, twenty/minute/IP.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Batch",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Batch"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, batch/API rights, or required membership/2FA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource outside key workspace or not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/idempotency/content/access conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Draft expired or discarded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, file, row structure, quota or explicit exclusions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "batch",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchMapping"
              },
              "example": {
                "revision": null,
                "mapping": {
                  "label": 0,
                  "destination": 1,
                  "reference": 2
                },
                "kind": "static",
                "template": "car"
              }
            }
          }
        },
        "operationId": "patch_batches_batch_mapping"
      }
    },
    "/batches/{batch}/confirm": {
      "post": {
        "summary": "Confirm and create batch codes atomically",
        "description": "Requires batches:write + codes:create and current batch/API access. Latest review revision required. Invalid rows need explicit exclude_invalid=true; quota/dynamic rights rechecked under sorted payer/workspace locks. Same revision may repeat idempotently, without restoring trashed codes. Original uploaded cells removed on completion. Ten prepares/minute/actor and payer, twenty/minute/IP.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Batch",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Batch"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, batch/API rights, or required membership/2FA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource outside key workspace or not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/idempotency/content/access conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Draft expired or discarded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, file, row structure, quota or explicit exclusions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "batch",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "minLength": 64,
                    "maxLength": 64
                  },
                  "exclude_invalid": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "operationId": "post_batches_batch_confirm"
      }
    },
    "/batches/{batch}/svg.zip": {
      "get": {
        "summary": "Export complete batch SVGs as ZIP",
        "description": "Requires batches:export + codes:export and current batch/API access. Complete saved drawings, caption and arrow. QR codes already trashed are skipped; an empty collection returns 422. Maximum 25 MB uncompressed SVG data. Safe filenames, private temporary file deleted after streaming. After render, current key/scopes/workspace/subscription/gifts/QR signatures rechecked before counting each SVG download. Each successful retry counts another export. Five/minute/actor, ten/minute/payer, twenty/minute/IP.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "ZIP archive",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, batch/API rights, or required membership/2FA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource outside key workspace or not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/idempotency/content/access conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Draft expired or discarded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, file, row structure, quota or explicit exclusions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "batch",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_batches_batch_svg_zip"
      }
    },
    "/batches/{batch}/manifest.csv": {
      "get": {
        "summary": "Download batch CSV summary",
        "description": "Requires batches:read. Completed batches only. UTF-8 BOM, semicolon-separated, CRLF. Formula prefixes neutralized. Normalized selected fields and historical row results; no QR download counted.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "CSV summary",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, batch/API rights, or required membership/2FA",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource outside key workspace or not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/idempotency/content/access conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Draft expired or discarded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, file, row structure, quota or explicit exclusions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "batch",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_batches_batch_manifest_csv"
      }
    },
    "/qr-codes/{code}/history": {
      "get": {
        "summary": "Read version history",
        "description": "Requires history:read. Metadata only: no content, snapshot, author identity or secrets. Latest 100 versions, 12 per page. Existing codes start with a reference on their first edit; no backfill of earlier content. Reading creates no version.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "responses": {
          "200": {
            "description": "Version metadata and current revision",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HistoryVersion"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "revision": {
                          "$ref": "#/components/schemas/HistoryRevision"
                        },
                        "current_page": {
                          "type": "integer"
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer",
                          "const": 100
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked API key."
          },
          "403": {
            "description": "Missing scope, role or effective entitlement."
          },
          "404": {
            "description": "Code or version absent, trashed, pruned or outside this workspace."
          },
          "409": {
            "description": "The QR changed. Read its current revision before retrying. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid input or incompatible fixed content."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "operationId": "get_qr_codes_code_history"
      }
    },
    "/qr-codes/{code}/history/{version}": {
      "get": {
        "summary": "Read a previous content and appearance",
        "description": "Requires history:read AND codes:read. Returns only historical label, kind, content_type, content, destination and style. Wi-Fi passwords and imported raw contents remain redacted, as in ordinary code metadata. Does not generate a file or increment downloads.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "responses": {
          "200": {
            "description": "Masked historical QR data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "number": {
                          "type": "integer"
                        },
                        "changes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "qr_code": {
                          "type": "object",
                          "description": "Only the historical content and appearance fields of QrCode; no secret, identity or access state.",
                          "properties": {
                            "label": {
                              "type": "string"
                            },
                            "kind": {
                              "type": "string"
                            },
                            "destination": {
                              "type": "string"
                            },
                            "content_type": {
                              "type": "string",
                              "enum": [
                                "url",
                                "text",
                                "wifi",
                                "vcard",
                                "email",
                                "phone",
                                "sms",
                                "whatsapp",
                                "raw"
                              ],
                              "default": "url"
                            },
                            "content": {
                              "type": "object",
                              "description": "Typed content fields. Wi-Fi passwords and raw imported content are omitted. Raw content is an empty object."
                            },
                            "style": {
                              "$ref": "#/components/schemas/QrAppearance"
                            }
                          }
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "revision": {
                          "$ref": "#/components/schemas/HistoryRevision"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked API key."
          },
          "403": {
            "description": "Missing scope, role or effective entitlement."
          },
          "404": {
            "description": "Code or version absent, trashed, pruned or outside this workspace."
          },
          "409": {
            "description": "The QR changed. Read its current revision before retrying. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid input or incompatible fixed content."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "operationId": "get_qr_codes_code_history_version"
      }
    },
    "/qr-codes/{code}/history/{version}/restore": {
      "post": {
        "summary": "Restore a previous QR version",
        "description": "Requires explicit history:restore. Restores name, content and design only. Preserves identity, short link, campaign, disabled state, schedule, fallback and statistics. Does not recover trash, create a code or occupy another slot. Current dynamic entitlement is required for dynamic QR. Static content cannot be changed. Writes a new version only if content/design/name differ; no-op returns changed=false. The mutation and history are atomic. No private content is returned, including for write-only keys.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "revision"
                ],
                "properties": {
                  "revision": {
                    "$ref": "#/components/schemas/HistoryRevision"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Restoration outcome and new current revision",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "id",
                        "changed",
                        "revision"
                      ],
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "changed": {
                          "type": "boolean"
                        },
                        "revision": {
                          "$ref": "#/components/schemas/HistoryRevision"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked API key."
          },
          "403": {
            "description": "Missing scope, role or effective entitlement."
          },
          "404": {
            "description": "Code or version absent, trashed, pruned or outside this workspace."
          },
          "409": {
            "description": "The QR changed. Read its current revision before retrying. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid input or incompatible fixed content."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "operationId": "post_qr_codes_code_history_version_restore"
      }
    },
    "/style-kits": {
      "get": {
        "summary": "List reusable QR styles",
        "description": "Requires kits:read. Current workspace only, 12 per page. archived=1 selects archives. All reads are private/no-store.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "archived",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                0,
                1
              ],
              "default": 0
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "responses": {
          "200": {
            "description": "Style reference only with kits:write; full style also requires kits:read.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/StyleKit"
                          }
                        },
                        "current_page": {
                          "type": "integer"
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Current API rights and explicit scope required. Company owner/admin only."
          },
          "404": {
            "description": "Not in the key workspace."
          },
          "409": {
            "description": "Stale revision or idempotency key reused with a different body. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Archived style cannot be edited or reused."
          },
          "422": {
            "description": "Invalid body, colours or technical capacity limit."
          },
          "429": {
            "description": "Rate limit exceeded. Retry-After is returned."
          }
        },
        "operationId": "get_style_kits"
      },
      "post": {
        "summary": "Create a reusable QR style",
        "description": "Requires kits:write, API entitlement and company owner/admin. All seven style fields and name required; unknown fields refused. 100 active/250 retained per workspace, archives included. No QR creation or commercial feature grant. 10 writes/min/actor, 20/payer and 30/IP, plus global API limits. Identical nonce/body returns 200; creation 201; changed body 409; archived nonce 410. Copy returned style into QR or batch creation; later kit edits never rewrite existing QR codes. Request body is limited to 8 KiB.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StyleKitInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/StyleKit"
                        },
                        {
                          "$ref": "#/components/schemas/StyleKitReference"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Style reference only with kits:write; full style also requires kits:read.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/StyleKit"
                        },
                        {
                          "$ref": "#/components/schemas/StyleKitReference"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Current API rights and explicit scope required. Company owner/admin only."
          },
          "404": {
            "description": "Not in the key workspace."
          },
          "409": {
            "description": "Stale revision or idempotency key reused with a different body. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Archived style cannot be edited or reused."
          },
          "413": {
            "description": "Request body exceeds 8 KiB."
          },
          "422": {
            "description": "Invalid body, colours or technical capacity limit."
          },
          "429": {
            "description": "Rate limit exceeded. Retry-After is returned."
          }
        },
        "operationId": "post_style_kits"
      }
    },
    "/style-kits/{id}": {
      "get": {
        "summary": "Read one style, including an archived style",
        "description": "Requires kits:read. Content never inherited by legacy qr:* permissions.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "responses": {
          "200": {
            "description": "Style reference only with kits:write; full style also requires kits:read.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/StyleKit"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Current API rights and explicit scope required. Company owner/admin only."
          },
          "404": {
            "description": "Not in the key workspace."
          },
          "409": {
            "description": "Stale revision or idempotency key reused with a different body. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Archived style cannot be edited or reused."
          },
          "422": {
            "description": "Invalid body, colours or technical capacity limit."
          },
          "429": {
            "description": "Rate limit exceeded. Retry-After is returned."
          }
        },
        "operationId": "get_style_kits_id"
      },
      "put": {
        "summary": "Replace a QR style",
        "description": "Requires kits:write. Full replacement with all seven style fields, name and current revision UUID. No changes to existing QR codes. Request body is limited to 8 KiB.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StyleKitUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Style reference only with kits:write; full style also requires kits:read.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/StyleKit"
                        },
                        {
                          "$ref": "#/components/schemas/StyleKitReference"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Current API rights and explicit scope required. Company owner/admin only."
          },
          "404": {
            "description": "Not in the key workspace."
          },
          "409": {
            "description": "Stale revision or idempotency key reused with a different body. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Archived style cannot be edited or reused."
          },
          "413": {
            "description": "Request body exceeds 8 KiB."
          },
          "422": {
            "description": "Invalid body, colours or technical capacity limit."
          },
          "429": {
            "description": "Rate limit exceeded. Retry-After is returned."
          }
        },
        "operationId": "put_style_kits_id"
      }
    },
    "/style-kits/{id}/archive": {
      "post": {
        "summary": "Archive a QR style",
        "description": "Requires kits:write and current revision UUID. Archiving is recoverable. QR codes using a copied style stay unchanged. Restoring also checks active capacity. Request body is limited to 8 KiB.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Style reference only with kits:write; full style also requires kits:read.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/StyleKit"
                        },
                        {
                          "$ref": "#/components/schemas/StyleKitReference"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Current API rights and explicit scope required. Company owner/admin only."
          },
          "404": {
            "description": "Not in the key workspace."
          },
          "409": {
            "description": "Stale revision or idempotency key reused with a different body. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Archived style cannot be edited or reused."
          },
          "413": {
            "description": "Request body exceeds 8 KiB."
          },
          "422": {
            "description": "Invalid body, colours or technical capacity limit."
          },
          "429": {
            "description": "Rate limit exceeded. Retry-After is returned."
          }
        },
        "operationId": "post_style_kits_id_archive"
      }
    },
    "/style-kits/{id}/restore": {
      "post": {
        "summary": "Restore a QR style",
        "description": "Requires kits:write and current revision UUID. Archiving is recoverable. QR codes using a copied style stay unchanged. Restoring also checks active capacity. Request body is limited to 8 KiB.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Style reference only with kits:write; full style also requires kits:read.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/StyleKit"
                        },
                        {
                          "$ref": "#/components/schemas/StyleKitReference"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Current API rights and explicit scope required. Company owner/admin only."
          },
          "404": {
            "description": "Not in the key workspace."
          },
          "409": {
            "description": "Stale revision or idempotency key reused with a different body. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Archived style cannot be edited or reused."
          },
          "413": {
            "description": "Request body exceeds 8 KiB."
          },
          "422": {
            "description": "Invalid body, colours or technical capacity limit."
          },
          "429": {
            "description": "Rate limit exceeded. Retry-After is returned."
          }
        },
        "operationId": "post_style_kits_id_restore"
      }
    },
    "/pages": {
      "get": {
        "summary": "List page metadata, 12 per page",
        "description": "Requires pages:read. Bound to the current personal/company workspace. Current rights are rechecked on writes. No private content is returned to write-only keys.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private response, no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/HostedPage"
                          }
                        },
                        "current_page": {
                          "type": "integer"
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid/revoked key."
          },
          "403": {
            "description": "Missing scope, current entitlement or workspace role."
          },
          "404": {
            "description": "Page/version not found in the bound workspace."
          },
          "409": {
            "description": "Revision changed. Read the current page before resubmission. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Raw request exceeds 128 KiB."
          },
          "422": {
            "description": "Invalid content, unknown fields or missing explicit confirmation."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After. Page writes additionally limited to 30/min actor, 60/min payer and 90/min IP."
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_pages"
      }
    },
    "/pages/{page}": {
      "get": {
        "summary": "Read draft, current revision and preview token",
        "description": "Requires pages:read. Bound to the current personal/company workspace. Current rights are rechecked on writes. No private content is returned to write-only keys.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private response, no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HostedPage"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid/revoked key."
          },
          "403": {
            "description": "Missing scope, current entitlement or workspace role."
          },
          "404": {
            "description": "Page/version not found in the bound workspace."
          },
          "409": {
            "description": "Revision changed. Read the current page before resubmission. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Raw request exceeds 128 KiB."
          },
          "422": {
            "description": "Invalid content, unknown fields or missing explicit confirmation."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After. Page writes additionally limited to 30/min actor, 60/min payer and 90/min IP."
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_pages_page"
      },
      "put": {
        "summary": "Save a language into the private draft",
        "description": "Requires pages:write. Bound to the current personal/company workspace. Current rights are rechecked on writes. No private content is returned to write-only keys.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private response, no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HostedPage"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid/revoked key."
          },
          "403": {
            "description": "Missing scope, current entitlement or workspace role."
          },
          "404": {
            "description": "Page/version not found in the bound workspace."
          },
          "409": {
            "description": "Revision changed. Read the current page before resubmission. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Raw request exceeds 128 KiB."
          },
          "422": {
            "description": "Invalid content, unknown fields or missing explicit confirmation."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After. Page writes additionally limited to 30/min actor, 60/min payer and 90/min IP."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostedPageSave"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "operationId": "put_pages_page"
      }
    },
    "/pages/{page}/preview": {
      "get": {
        "summary": "Render private saved-draft HTML without openings or click tracking",
        "description": "Requires pages:read. Bound to the current personal/company workspace. Current rights are rechecked on writes. No private content is returned to write-only keys.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private response, no-store.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Invalid/revoked key."
          },
          "403": {
            "description": "Missing scope, current entitlement or workspace role."
          },
          "404": {
            "description": "Page/version not found in the bound workspace."
          },
          "409": {
            "description": "Revision changed. Read the current page before resubmission. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Raw request exceeds 128 KiB."
          },
          "422": {
            "description": "Invalid content, unknown fields or missing explicit confirmation."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After. Page writes additionally limited to 30/min actor, 60/min payer and 90/min IP."
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "revision",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_pages_page_preview"
      }
    },
    "/pages/{page}/publish": {
      "post": {
        "summary": "Publish all variants",
        "description": "Requires pages:publish. Bound to the current personal/company workspace. Current rights are rechecked on writes. No private content is returned to write-only keys.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private response, no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HostedPage"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid/revoked key."
          },
          "403": {
            "description": "Missing scope, current entitlement or workspace role."
          },
          "404": {
            "description": "Page/version not found in the bound workspace."
          },
          "409": {
            "description": "Revision changed. Read the current page before resubmission. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Raw request exceeds 128 KiB."
          },
          "422": {
            "description": "Invalid content, unknown fields or missing explicit confirmation."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After. Page writes additionally limited to 30/min actor, 60/min payer and 90/min IP."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostedPagePublication"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "operationId": "post_pages_page_publish"
      }
    },
    "/pages/{page}/unpublish": {
      "post": {
        "summary": "Withdraw the public page",
        "description": "Requires pages:publish. Bound to the current personal/company workspace. Current rights are rechecked on writes. No private content is returned to write-only keys.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private response, no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HostedPage"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid/revoked key."
          },
          "403": {
            "description": "Missing scope, current entitlement or workspace role."
          },
          "404": {
            "description": "Page/version not found in the bound workspace."
          },
          "409": {
            "description": "Revision changed. Read the current page before resubmission. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Raw request exceeds 128 KiB."
          },
          "422": {
            "description": "Invalid content, unknown fields or missing explicit confirmation."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After. Page writes additionally limited to 30/min actor, 60/min payer and 90/min IP."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostedPagePublication"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "operationId": "post_pages_page_unpublish"
      }
    },
    "/pages/{page}/locales/{locale}": {
      "delete": {
        "summary": "Remove a non-default draft variant; publication remains unchanged",
        "description": "Requires pages:write. Bound to the current personal/company workspace. Current rights are rechecked on writes. No private content is returned to write-only keys.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private response, no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HostedPage"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid/revoked key."
          },
          "403": {
            "description": "Missing scope, current entitlement or workspace role."
          },
          "404": {
            "description": "Page/version not found in the bound workspace."
          },
          "409": {
            "description": "Revision changed. Read the current page before resubmission. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Raw request exceeds 128 KiB."
          },
          "422": {
            "description": "Invalid content, unknown fields or missing explicit confirmation."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After. Page writes additionally limited to 30/min actor, 60/min payer and 90/min IP."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostedPageRevision"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "locale",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "operationId": "delete_pages_page_locales_locale"
      }
    },
    "/pages/{page}/history": {
      "get": {
        "summary": "List retained published snapshots and withdrawals",
        "description": "Requires pages:read. Bound to the current personal/company workspace. Current rights are rechecked on writes. No private content is returned to write-only keys.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private response, no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "number": {
                            "type": "integer"
                          },
                          "action": {
                            "enum": [
                              "publish",
                              "unpublish"
                            ]
                          },
                          "snapshot": {
                            "anyOf": [
                              {
                                "$ref": "#/components/schemas/HostedPageSnapshot"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid/revoked key."
          },
          "403": {
            "description": "Missing scope, current entitlement or workspace role."
          },
          "404": {
            "description": "Page/version not found in the bound workspace."
          },
          "409": {
            "description": "Revision changed. Read the current page before resubmission. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Raw request exceeds 128 KiB."
          },
          "422": {
            "description": "Invalid content, unknown fields or missing explicit confirmation."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After. Page writes additionally limited to 30/min actor, 60/min payer and 90/min IP."
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_pages_page_history"
      }
    },
    "/pages/{page}/history/{version}/restore": {
      "post": {
        "summary": "Restore a published version into the draft, without publishing",
        "description": "Requires pages:write. Bound to the current personal/company workspace. Current rights are rechecked on writes. No private content is returned to write-only keys.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private response, no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HostedPage"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid/revoked key."
          },
          "403": {
            "description": "Missing scope, current entitlement or workspace role."
          },
          "404": {
            "description": "Page/version not found in the bound workspace."
          },
          "409": {
            "description": "Revision changed. Read the current page before resubmission. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Raw request exceeds 128 KiB."
          },
          "422": {
            "description": "Invalid content, unknown fields or missing explicit confirmation."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After. Page writes additionally limited to 30/min actor, 60/min payer and 90/min IP."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostedPageRevision"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "operationId": "post_pages_page_history_version_restore"
      }
    },
    "/pages/{page}/media": {
      "get": {
        "summary": "List private page media",
        "description": "Requires pages:read. Bound to the key workspace. Hosted audio/video requires operator-configured FFmpeg/ffprobe and durable cache locks. Input10MiB; video90seconds and2048px, audio600seconds. Only local-file demuxing, no network. Normalized MP4/MP3 metadata stripped and output duration checked; UTF8 plain WebVTT32KiB/200cues. Metadata encrypted. Native/public file playback supports one bytes Range with current access checks, private/no-store/nosniff, If-Range mismatch returns full200; range requests never infer a play/conversion or QR download. No captions semantic accuracy or WCAG certification claimed.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "responses": {
          "200": {
            "description": "Private metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/HostedPageMedia"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope/current rights"
          },
          "404": {
            "description": "Not found in key workspace"
          },
          "409": {
            "description": "Revision or upload nonce conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Upload too large"
          },
          "422": {
            "description": "Invalid file, foreign reference, capacity, unknown field, confirmation missing or file still referenced"
          },
          "429": {
            "description": "Rate limit"
          },
          "503": {
            "description": "PDF worker unavailable"
          }
        },
        "operationId": "get_pages_page_media"
      },
      "post": {
        "summary": "Upload and reconstruct PNG/JPEG/PDF idempotently",
        "description": "Requires pages:write. Bound to the key workspace. Request UUID bound to source hash and supplied name. Current revision required; upload does not change draft, revision or publication. Technical limits: 20 files/100 MiB per page, input/output 10 MiB per file. Images at most 12 MP/12,000 px side, scaled to 1600 px and metadata discarded. PDF process bounded to 96 MiB/15 s/50 pages. Visible pages only; annotations, links, forms, signatures, document actions and source metadata discarded. Some encrypted/compressed PDFs unsupported. No antivirus or PDF-accessibility certification claimed. Raster processing uses the same isolated 96 MiB/15 s worker boundary. Linux native allocations/CPU additionally bounded through POSIX rlimits where available. Hosted audio/video requires operator-configured FFmpeg/ffprobe and durable cache locks. Input10MiB; video90seconds and2048px, audio600seconds. Only local-file demuxing, no network. Normalized MP4/MP3 metadata stripped and output duration checked; UTF8 plain WebVTT32KiB/200cues. Metadata encrypted. Native/public file playback supports one bytes Range with current access checks, private/no-store/nosniff, If-Range mismatch returns full200; range requests never infer a play/conversion or QR download. No captions semantic accuracy or WCAG certification claimed.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Created or identical immediate replay",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/HostedPageMedia"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope/current rights"
          },
          "404": {
            "description": "Not found in key workspace"
          },
          "409": {
            "description": "Revision or upload nonce conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Upload too large"
          },
          "422": {
            "description": "Invalid file, foreign reference, capacity, unknown field, confirmation missing or file still referenced"
          },
          "429": {
            "description": "Rate limit"
          },
          "503": {
            "description": "PDF worker unavailable"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "file",
                  "name",
                  "request_id",
                  "revision"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "request_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "operationId": "post_pages_page_media"
      }
    },
    "/pages/{page}/media/{asset}": {
      "get": {
        "summary": "Download a private normalized asset",
        "description": "Requires pages:read. Bound to the key workspace. Images inline; PDF attachment. Private no-store, no-referrer, sandbox, nosniff. No public statistics for private downloads. Hosted audio/video requires operator-configured FFmpeg/ffprobe and durable cache locks. Input10MiB; video90seconds and2048px, audio600seconds. Only local-file demuxing, no network. Normalized MP4/MP3 metadata stripped and output duration checked; UTF8 plain WebVTT32KiB/200cues. Metadata encrypted. Native/public file playback supports one bytes Range with current access checks, private/no-store/nosniff, If-Range mismatch returns full200; range requests never infer a play/conversion or QR download. No captions semantic accuracy or WCAG certification claimed.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "asset",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "responses": {
          "200": {
            "description": "File",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope/current rights"
          },
          "404": {
            "description": "Not found in key workspace"
          },
          "409": {
            "description": "Revision or upload nonce conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Upload too large"
          },
          "422": {
            "description": "Invalid file, foreign reference, capacity, unknown field, confirmation missing or file still referenced"
          },
          "429": {
            "description": "Rate limit"
          },
          "503": {
            "description": "PDF worker unavailable"
          }
        },
        "operationId": "get_pages_page_media_asset"
      },
      "delete": {
        "summary": "Permanently remove an unused asset",
        "description": "Requires pages:write. Bound to the key workspace. Explicit confirmation required. Refused while referenced in the draft or any retained publication version (up to 30). Does not publish changes.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "asset",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Unused media removed"
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope/current rights"
          },
          "404": {
            "description": "Not found in key workspace"
          },
          "409": {
            "description": "Revision or upload nonce conflict Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Upload too large"
          },
          "422": {
            "description": "Invalid file, foreign reference, capacity, unknown field, confirmation missing or file still referenced"
          },
          "429": {
            "description": "Rate limit"
          },
          "503": {
            "description": "PDF worker unavailable"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision",
                  "confirmed"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "confirmed": {
                    "type": "boolean",
                    "const": true
                  }
                }
              }
            }
          }
        },
        "operationId": "delete_pages_page_media_asset"
      }
    },
    "/feedback": {
      "get": {
        "summary": "List feedback and honest rating summary",
        "description": "Requires feedback:read. Current owner/admin API access is rechecked; legacy QR scopes grant no feedback permissions. Private no-store response. Existing submissions remain available until expiry after a plan downgrade. Summary uses the same selected filters as the list. Average is the mean of rating_units / 2 from rated retained responses only; unrated responses are excluded. No population-wide satisfaction claim.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "summary": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "pending": {
                          "type": "integer"
                        },
                        "rated": {
                          "type": "integer"
                        },
                        "average": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "minimum": 0.5,
                          "maximum": 5
                        }
                      }
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/FeedbackEntry"
                          }
                        },
                        "current_page": {
                          "type": "integer"
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "per_page": {
                          "type": "integer",
                          "const": 20
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing dedicated scope or current workspace rights."
          },
          "404": {
            "description": "Foreign, deleted or expired response."
          },
          "409": {
            "description": "Revision changed; read current state before editing. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Write body exceeds 4 KiB."
          },
          "422": {
            "description": "Invalid fields, unknown fields or unconfirmed deletion."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "page_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "new",
                "in_progress",
                "resolved"
              ]
            }
          },
          {
            "name": "purpose",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "feedback",
                "quote",
                "signup",
                "report"
              ]
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_feedback"
      }
    },
    "/feedback/{entry}": {
      "get": {
        "summary": "Read a private feedback response",
        "description": "Requires feedback:read. Current owner/admin API access is rechecked; legacy QR scopes grant no feedback permissions. Private no-store response. Existing submissions remain available until expiry after a plan downgrade.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/FeedbackEntry"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing dedicated scope or current workspace rights."
          },
          "404": {
            "description": "Foreign, deleted or expired response."
          },
          "409": {
            "description": "Revision changed; read current state before editing. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Write body exceeds 4 KiB."
          },
          "422": {
            "description": "Invalid fields, unknown fields or unconfirmed deletion."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "parameters": [
          {
            "name": "entry",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_feedback_entry"
      },
      "patch": {
        "summary": "Process feedback with revision protection",
        "description": "Requires feedback:write. Current owner/admin API access is rechecked; legacy QR scopes grant no feedback permissions. Private no-store response. Existing submissions remain available until expiry after a plan downgrade.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "new",
                            "in_progress",
                            "resolved"
                          ]
                        },
                        "revision": {
                          "type": "string",
                          "format": "uuid"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing dedicated scope or current workspace rights."
          },
          "404": {
            "description": "Foreign, deleted or expired response."
          },
          "409": {
            "description": "Revision changed; read current state before editing. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Write body exceeds 4 KiB."
          },
          "422": {
            "description": "Invalid fields, unknown fields or unconfirmed deletion."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackUpdate"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "entry",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "operationId": "patch_feedback_entry"
      },
      "delete": {
        "summary": "Delete feedback permanently",
        "description": "Requires feedback:write. Current owner/admin API access is rechecked; legacy QR scopes grant no feedback permissions. Private no-store response. Existing submissions remain available until expiry after a plan downgrade. Explicit confirmation is required. A short-lived submission receipt prevents HTTP retries from recreating erased personal data.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "204": {
            "description": "Success."
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing dedicated scope or current workspace rights."
          },
          "404": {
            "description": "Foreign, deleted or expired response."
          },
          "409": {
            "description": "Revision changed; read current state before editing. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Write body exceeds 4 KiB."
          },
          "422": {
            "description": "Invalid fields, unknown fields or unconfirmed deletion."
          },
          "429": {
            "description": "Rate limit. Respect Retry-After."
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackDelete"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "entry",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "operationId": "delete_feedback_entry"
      }
    },
    "/feedback/{entry}/replies": {
      "post": {
        "summary": "Queue a reply to a declared visitor email",
        "description": "Explicit mail to the email declared on this response; no arbitrary to/from/header field. Current editor or manager rights required. Reuse request_id and identical message for idempotent retries. Changed payload or stale revision: 409. Maximum 10 replies per entry, 3 per recipient and 50 per workspace per UTC day. Responses follow the entry retention, encrypted at rest. SMTP uncertainty is never automatically retried. Requires feedback:reply; feedback:write and legacy QR scopes grant no sending rights. HTTP 202 is queued acceptance, not inbox delivery. sent means accepted by SMTP. uncertain means the server may have accepted it; no automatic resend. Optional multipart attachment, encrypted, sent only with this explicit reply; quotas10files/entry and100MiB/page.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted or identical idempotent retry",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/FeedbackReplyResource"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or expired key"
          },
          "403": {
            "description": "Missing permission or revoked access"
          },
          "404": {
            "description": "Unknown, expired or foreign response"
          },
          "409": {
            "description": "Changed revision or idempotency payload Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid fields or confirmation"
          },
          "429": {
            "description": "Sending budget exhausted"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackReply"
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "request_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000
                  },
                  "confirmed": {
                    "type": "boolean",
                    "const": true
                  },
                  "attachment": {
                    "type": "string",
                    "format": "binary",
                    "description": "Optional normalized PNG/JPEG/PDF <=2MiB, private. Same file bytes required for idempotent replay."
                  }
                },
                "required": [
                  "revision",
                  "request_id",
                  "message",
                  "confirmed"
                ],
                "description": "Explicit mail to the email declared on this response; no arbitrary to/from/header field. Current editor or manager rights required. Reuse request_id and identical message for idempotent retries. Changed payload or stale revision: 409. Maximum 10 replies per entry, 3 per recipient and 50 per workspace per UTC day. Responses follow the entry retention, encrypted at rest. SMTP uncertainty is never automatically retried."
              }
            }
          }
        },
        "parameters": [
          {
            "name": "entry",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "operationId": "post_feedback_entry_replies"
      }
    },
    "/feedback/preferences": {
      "get": {
        "summary": "Read my feedback mail preferences",
        "description": "Requires feedback:notify. Self only; does not disclose visitor contacts. Defaults to off; no notification to a user-selected arbitrary address.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Personal preferences for the key workspace",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/FeedbackMailPreference"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission required"
          },
          "409": {
            "description": "Conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          }
        },
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_feedback_preferences"
      },
      "put": {
        "summary": "Configure my feedback mail preferences",
        "description": "Self only, current verified account address; company/personal boundary comes from the API key. Omit/null revision only for first creation, otherwise current revision required. Complete IANA timezone identifier. Changing settings invalidates pending notifications. Daily at 08:00 local, weekly Monday 08:00; aggregation counts new retained entries, rated count separately, no copied private message. Instant maximum 20/day. Off by default. Requires feedback:notify.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Personal preferences for the key workspace",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/FeedbackMailPreference"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission required"
          },
          "409": {
            "description": "Stale revision Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid mode, timezone or unknown fields"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackMailPreferenceUpdate"
              }
            }
          }
        },
        "operationId": "put_feedback_preferences"
      }
    },
    "/restaurants": {
      "get": {
        "summary": "List menu pages and current menu revisions",
        "description": "Requires restaurants:read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data contains Laravel paginated page summaries, 20 per page."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:read",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_restaurants"
      }
    },
    "/restaurants/{page}": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Read menu draft, published snapshot and actor-bound preview token",
        "description": "Requires restaurants:read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Workspace result"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:read",
        "parameters": [
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_restaurants_page"
      },
      "put": {
        "summary": "Save the menu draft for a locale",
        "description": "Requires restaurants:write. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: id, revision, published_revision only; no publication implied."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RestaurantMenuInput"
              }
            }
          }
        },
        "operationId": "put_restaurants_page"
      }
    },
    "/restaurants/{page}/preview": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Inspect the saved menu and obtain preview token",
        "description": "Requires restaurants:read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: canonical draft; preview_token separate. Exact revision required."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:read",
        "parameters": [
          {
            "name": "revision",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_restaurants_page_preview"
      }
    },
    "/restaurants/{page}/publish": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Publish selectable menu",
        "description": "Requires restaurants:publish. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: id, revision, published_revision."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:publish",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RestaurantPublication"
              }
            }
          }
        },
        "operationId": "post_restaurants_page_publish"
      }
    },
    "/restaurants/{page}/withdraw": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Withdraw selectable menu",
        "description": "Requires restaurants:publish. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: id, revision, published_revision."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:publish",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RestaurantPublication"
              }
            }
          }
        },
        "operationId": "post_restaurants_page_withdraw"
      }
    },
    "/restaurants/{page}/preparation": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Aggregate confirmed dish quantities for a service without contacts",
        "description": "Requires restaurants:requests_read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Preparation aggregate, confirmed only, integer counts/option combinations. starts_at pins historical service even if slot UUID is reused. No customer names, notes or email addresses."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:requests_read",
        "parameters": [
          {
            "name": "slot_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "starts_at",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_restaurants_page_preparation"
      }
    },
    "/restaurant-requests": {
      "get": {
        "summary": "List private requests, service-date filters and summary",
        "description": "Requires restaurants:requests_read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "summary pending/confirmed/guests plus data Laravel paginator of RestaurantRequest, 20 per page. date interpreted per menu timezone."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:requests_read",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "menu_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_restaurant_requests"
      },
      "post": {
        "summary": "Create a pending request through a trusted integration",
        "description": "Requires restaurants:requests_create. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "202": {
            "description": "data: id, status, revision only; data:null for erased idempotent request. No private follow-up URL."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:requests_create",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RestaurantRequestInput"
              }
            }
          }
        },
        "operationId": "post_restaurant_requests"
      }
    },
    "/restaurant-requests/{booking}": {
      "parameters": [
        {
          "name": "booking",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Read request, frozen selection and history",
        "description": "Requires restaurants:requests_read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: RestaurantRequest"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:requests_read",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_restaurant_requests_booking"
      },
      "delete": {
        "summary": "Erase a terminal request and associated quantities and mail",
        "description": "Requires restaurants:requests_write. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "204": {
            "description": "No body; request must be declined, cancelled or completed. Revision and confirmed required in JSON body. Erasure receipt persists to prevent recreation."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:requests_write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision",
                  "confirmed"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "confirmed": {
                    "const": true
                  }
                }
              }
            }
          }
        },
        "operationId": "delete_restaurant_requests_booking"
      }
    },
    "/restaurant-requests/{booking}/decision": {
      "parameters": [
        {
          "name": "booking",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Manually confirm, propose, decline, cancel or complete",
        "description": "Requires restaurants:requests_write. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: id, status, revision only."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "restaurants:requests_write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RestaurantDecision"
              }
            }
          }
        },
        "operationId": "post_restaurant_requests_booking_decision"
      }
    },
    "/restaurants/{page}/preparation.pdf": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Download a private service preparation PDF",
        "description": "Requires restaurants:export (independent from requests_read). Confirmed quantities only, no customer names/notes/emails. Dated service; max 200 grouped lines and 128 KiB aggregate. A fresh snapshot and role/key are checked after rendering: change returns 409 with no counter increment. Counts prepared PDFs per menu; distinct from QR download analytics. Remote resources, JavaScript and PHP disabled in PDF renderer. Instantaneous snapshot, check cancellations before service.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "slot_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "starts_at",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "responses": {
          "200": {
            "description": "Private PDF attachment; menu preparation_downloads incremented after final checks.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "403": {
            "description": "Scope/current role or entitlement refused"
          },
          "404": {
            "description": "Missing or foreign resource"
          },
          "409": {
            "description": "Preparation changed while rendering; retry after reading current service Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid date/service or bounded PDF size exceeded"
          },
          "429": {
            "description": "Export rate limit"
          }
        },
        "operationId": "get_restaurants_page_preparation_pdf"
      }
    },
    "/journeys": {
      "get": {
        "summary": "List menu pages and current menu revisions",
        "description": "Requires journeys:read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data contains Laravel paginated page summaries, 20 per page."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:read",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_journeys"
      }
    },
    "/journeys/{page}": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Read catalogue draft, published snapshot and actor-bound preview token",
        "description": "Requires journeys:read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Workspace result"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:read",
        "parameters": [
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_journeys_page"
      },
      "put": {
        "summary": "Save the catalogue draft for a locale",
        "description": "Requires journeys:write. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: id, revision, published_revision only; no publication implied."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JourneyMenuInput"
              }
            }
          }
        },
        "operationId": "put_journeys_page"
      }
    },
    "/journeys/{page}/preview": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Inspect the saved menu and obtain preview token",
        "description": "Requires journeys:read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: canonical draft; preview_token separate. Exact revision required."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:read",
        "parameters": [
          {
            "name": "revision",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_journeys_page_preview"
      }
    },
    "/journeys/{page}/publish": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Publish selectable menu",
        "description": "Requires journeys:publish. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: id, revision, published_revision."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:publish",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JourneyPublication"
              }
            }
          }
        },
        "operationId": "post_journeys_page_publish"
      }
    },
    "/journeys/{page}/withdraw": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Withdraw selectable menu",
        "description": "Requires journeys:publish. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: id, revision, published_revision."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:publish",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JourneyPublication"
              }
            }
          }
        },
        "operationId": "post_journeys_page_withdraw"
      }
    },
    "/journey-requests": {
      "get": {
        "summary": "List private requests, service-date filters and summary",
        "description": "Requires journeys:requests_read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "summary pending/confirmed/guests plus data Laravel paginator of JourneyRequest, 20 per page. date interpreted per menu timezone."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:requests_read",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "menu_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_journey_requests"
      },
      "post": {
        "summary": "Create a pending request through a trusted integration",
        "description": "Requires journeys:requests_create. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "202": {
            "description": "data: id, status, revision only; data:null for erased idempotent request. No private follow-up URL."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:requests_create",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JourneyRequestInput"
              }
            }
          }
        },
        "operationId": "post_journey_requests"
      }
    },
    "/journey-requests/{booking}": {
      "parameters": [
        {
          "name": "booking",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Read request, frozen selection and history",
        "description": "Requires journeys:requests_read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: JourneyRequest"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:requests_read",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_journey_requests_booking"
      },
      "delete": {
        "summary": "Erase a terminal request and associated quantities and mail",
        "description": "Requires journeys:requests_write. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "204": {
            "description": "No body; request must be declined, cancelled or completed. Revision and confirmed required in JSON body. Erasure receipt persists to prevent recreation."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:requests_write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision",
                  "confirmed"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "confirmed": {
                    "const": true
                  }
                }
              }
            }
          }
        },
        "operationId": "delete_journey_requests_booking"
      }
    },
    "/journey-requests/{booking}/decision": {
      "parameters": [
        {
          "name": "booking",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Manually confirm, propose, decline, cancel or complete",
        "description": "Requires journeys:requests_write. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: id, status, revision only."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:requests_write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JourneyDecision"
              }
            }
          }
        },
        "operationId": "post_journey_requests_booking_decision"
      }
    },
    "/journey-requests/{booking}/assignment": {
      "parameters": [
        {
          "name": "booking",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Assign a request to a current authorised editor or manager",
        "description": "Requires journeys:requests_write. Fresh user/company/location rights and quota checked on the server. Private no-store.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: JourneyRequest"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:requests_write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision",
                  "confirmed"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "confirmed": {
                    "type": "boolean",
                    "const": true
                  },
                  "assigned_user_id": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "operationId": "post_journey_requests_booking_assignment"
      }
    },
    "/journey-requests/{booking}/external-status": {
      "parameters": [
        {
          "name": "booking",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Record an event from the authorised business integration, separately from confirmation or payment",
        "description": "Requires journeys:integration. Fresh user/company/location rights and quota checked on the server. Private no-store.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: JourneyRequest"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:integration",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision",
                  "event_id",
                  "state",
                  "confirmed"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "confirmed": {
                    "type": "boolean",
                    "const": true
                  },
                  "event_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "state": {
                    "type": "string",
                    "enum": [
                      "accepted",
                      "in_transit",
                      "delivered",
                      "cancelled"
                    ]
                  },
                  "tracking_url": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri",
                    "maxLength": 1000
                  }
                }
              }
            }
          }
        },
        "operationId": "post_journey_requests_booking_external_status"
      }
    },
    "/locations": {
      "get": {
        "summary": "List authorised business locations",
        "description": "Requires locations:read. Fresh user/company/location rights and quota checked on the server. Private no-store.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: JourneyRequest"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "locations:read",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_locations"
      },
      "post": {
        "summary": "Create a business location; does not create another QR quota",
        "description": "Requires locations:write. Fresh user/company/location rights and quota checked on the server. Private no-store.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: JourneyRequest"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "locations:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "name",
                  "timezone"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "reference": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 40
                  },
                  "address": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 240
                  },
                  "city": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 100
                  },
                  "postal_code": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 20
                  },
                  "country": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "pattern": "^[A-Z]{2}$"
                  },
                  "timezone": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "operationId": "post_locations"
      }
    },
    "/locations/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Read an authorised location",
        "description": "Requires locations:read. Fresh user/company/location rights and quota checked on the server. Private no-store.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: JourneyRequest"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "locations:read",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_locations_id"
      },
      "put": {
        "summary": "Update a location with its current revision",
        "description": "Requires locations:write. Fresh user/company/location rights and quota checked on the server. Private no-store.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: JourneyRequest"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "locations:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "name",
                  "timezone"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "reference": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 40
                  },
                  "address": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 240
                  },
                  "city": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 100
                  },
                  "postal_code": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 20
                  },
                  "country": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "pattern": "^[A-Z]{2}$"
                  },
                  "timezone": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "operationId": "put_locations_id"
      }
    },
    "/locations/{id}/archive": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Archive or restore a location without disabling printed QR codes",
        "description": "Requires locations:write. Fresh user/company/location rights and quota checked on the server. Private no-store.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: JourneyRequest"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "locations:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision",
                  "confirmed",
                  "archived"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "confirmed": {
                    "type": "boolean",
                    "const": true
                  },
                  "archived": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "operationId": "post_locations_id_archive"
      }
    },
    "/locations/assignment": {
      "post": {
        "summary": "Move a QR to a location; retained requests prevent a transfer of private history",
        "description": "Requires locations:write. Fresh user/company/location rights and quota checked on the server. Private no-store.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "data: JourneyRequest"
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "locations:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "code_id",
                  "updated_at",
                  "assignment_token",
                  "confirmed"
                ],
                "properties": {
                  "code_id": {
                    "type": "integer"
                  },
                  "updated_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "assignment_token": {
                    "type": "string",
                    "minLength": 64,
                    "maxLength": 64
                  },
                  "location_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  },
                  "confirmed": {
                    "type": "boolean",
                    "const": true
                  }
                }
              }
            }
          }
        },
        "operationId": "post_locations_assignment"
      }
    },
    "/journeys/{page}/preparation": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Aggregate confirmed dish quantities for a service without contacts",
        "description": "Requires journeys:requests_read. Personal/company isolation and fresh permissions enforced. Private no-store response.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Preparation aggregate, confirmed only, integer counts/option combinations. starts_at pins historical service even if slot UUID is reused. No customer names, notes or email addresses."
          },
          "401": {
            "description": "Bearer key missing or invalid"
          },
          "403": {
            "description": "Current key scope, role, 2FA or payer entitlement refused"
          },
          "404": {
            "description": "Missing or foreign workspace resource"
          },
          "409": {
            "description": "Stale revision/menu, changed quote, invalid transition or different idempotent command Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "410": {
            "description": "Public QR/page unavailable"
          },
          "413": {
            "description": "Bounded body size exceeded"
          },
          "422": {
            "description": "Invalid fields, service, stock, capacity or amount"
          },
          "429": {
            "description": "Rate/retention/history/mail limit reached"
          }
        },
        "x-scope": "journeys:requests_read",
        "parameters": [
          {
            "name": "slot_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Omit for undated confirmed requests; otherwise select the exact service."
          },
          {
            "name": "starts_at",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "operationId": "get_journeys_page_preparation"
      }
    },
    "/journeys/{page}/preparation.pdf": {
      "parameters": [
        {
          "name": "page",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Download a private service preparation PDF",
        "description": "Requires journeys:export (independent from requests_read). Confirmed quantities only, no customer names/notes/emails. Dated service; max 200 grouped lines and 128 KiB aggregate. A fresh snapshot and role/key are checked after rendering: change returns 409 with no counter increment. Counts prepared PDFs per menu; distinct from QR download analytics. Remote resources, JavaScript and PHP disabled in PDF renderer. Instantaneous snapshot, check cancellations before service.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "slot_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Omit for undated confirmed requests; otherwise select the exact service."
          },
          {
            "name": "starts_at",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Select an authorised location. A location-bound key cannot select another one."
          }
        ],
        "responses": {
          "200": {
            "description": "Private PDF attachment; menu preparation_downloads incremented after final checks.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "403": {
            "description": "Scope/current role or entitlement refused"
          },
          "404": {
            "description": "Missing or foreign resource"
          },
          "409": {
            "description": "Preparation changed while rendering; retry after reading current service Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid date/service or bounded PDF size exceeded"
          },
          "429": {
            "description": "Export rate limit"
          }
        },
        "operationId": "get_journeys_page_preparation_pdf"
      }
    },
    "/agency/clients": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "List client workspaces within the authenticated agency. Personal keys only.",
        "description": "Required scope: `agency:read`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_get_agency_clients",
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": []
      },
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Create a client workspace idempotently by request_id. All workspaces share one allowance.",
        "description": "Required scope: `agency:write`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_post_agency_clients",
        "responses": {
          "201": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgencyClientCreate"
              }
            }
          }
        }
      }
    },
    "/agency/clients/{client}": {
      "patch": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Change client cap or archive state using its current revision.",
        "description": "Required scope: `agency:write`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_patch_agency_clients_client",
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "client",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgencyClientUpdate"
              }
            }
          }
        }
      }
    },
    "/reviews": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "List reviews assigned to or created by this actor in the current workspace.",
        "description": "Required scope: `reviews:read`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_get_reviews",
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": []
      },
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Create an authenticated, assigned QR artwork review. Replays do not reopen an approved review.",
        "description": "Required scope: `reviews:write`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_post_reviews",
        "responses": {
          "201": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientReviewCreate"
              }
            }
          }
        }
      }
    },
    "/reviews/{review}": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Read assigned review. Changed, expired or revoked reviews are refused.",
        "description": "Required scope: `reviews:read`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_get_reviews_review",
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "review",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/reviews/{review}/decision": {
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Explicit approval or changes requested by the assigned reviewer only.",
        "description": "Required scope: `reviews:decide`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_post_reviews_review_decision",
        "responses": {
          "204": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "review",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientReviewDecision"
              }
            }
          }
        }
      }
    },
    "/reviews/{review}/revoke": {
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Revoke an agency-created review.",
        "description": "Required scope: `reviews:write`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_post_reviews_review_revoke",
        "responses": {
          "204": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "review",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/reviews/{review}/delivery.svg": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Download the approved unchanged artwork; increments a separate delivery counter.",
        "description": "Required scope: `reviews:export`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_get_reviews_review_delivery_svg",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "review",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/domains": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "List workspace domains and their DNS setup.",
        "description": "Required scope: `domains:read`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_get_domains",
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": []
      },
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Register a domain idempotently within the same workspace. Does not activate it.",
        "description": "Required scope: `domains:write`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_post_domains",
        "responses": {
          "201": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainCreate"
              }
            }
          }
        }
      }
    },
    "/domains/{domain}/check": {
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Queue DNS and HTTPS verification. Operator hosting configuration must be ready.",
        "description": "Required scope: `domains:write`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_post_domains_domain_check",
        "responses": {
          "202": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Revision"
              }
            }
          }
        }
      }
    },
    "/domains/{domain}/disable": {
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Disable serving; retains the global hostname reservation.",
        "description": "Required scope: `domains:write`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_post_domains_domain_disable",
        "responses": {
          "204": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Revision"
              }
            }
          }
        }
      }
    },
    "/domains/{domain}/enable": {
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Restart verification with a new TXT proof. Existing printed addresses stay unchanged.",
        "description": "Required scope: `domains:write`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_post_domains_domain_enable",
        "responses": {
          "204": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Revision"
              }
            }
          }
        }
      }
    },
    "/agency/clients/adopt": {
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Attach an existing company owned by the agency, preserving its team, QR codes and links.",
        "description": "Requires agency:write on a personal agency key. Only companies already owned by that payer may be attached. Internal service applications are excluded. request_id is idempotent; the cap must cover current non-trashed QR codes and fit the shared allowance.",
        "operationId": "agency_adopt_client",
        "responses": {
          "200": {
            "description": "The existing company attached as a client. An identical request_id returns the same client without resetting later changes.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AgencyClient"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgencyClientAdopt"
              },
              "example": {
                "request_id": "988272d3-47a4-4c40-b219-88461fdf41ba",
                "company_id": 12,
                "quota": 150
              }
            }
          }
        }
      }
    },
    "/agency/eligible-companies": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "List existing companies that this payer may attach, with active QR counts.",
        "description": "Required scope: `agency:read`. All account, workspace and location restrictions are enforced server-side.",
        "operationId": "agency_eligible_companies",
        "responses": {
          "200": {
            "description": "Companies already owned by the agency that may be attached. Internal service applications and client spaces are excluded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "current_page": {
                          "type": "integer"
                        },
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/AgencyEligibleCompany"
                          }
                        },
                        "total": {
                          "type": "integer"
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "per_page": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "current_page": 1,
                    "data": [
                      {
                        "id": 12,
                        "name": "Example studio",
                        "used": 8
                      }
                    ],
                    "total": 1,
                    "last_page": 1,
                    "per_page": 50
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "in": "query",
            "name": "q",
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Literal company name search; wildcard characters are not interpreted."
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ]
      }
    },
    "/reviews/{review}/delivery.png": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Download approved artwork as PNG",
        "description": "Requires reviews:export. Assigned reviewer or owning agency only, with current membership, active key, unchanged content and a valid approval. The complete approved composition is preserved. Artwork is generated outside SQL locks, then access and the approval revision are checked again before counting or releasing bytes. Renderer changes that alter the approved SVG require a new approval.",
        "operationId": "agency_deliver_review_png",
        "responses": {
          "200": {
            "description": "Approved artwork. Private, no-store. Counted separately from ordinary QR exports.",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "review",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "size",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                512,
                1024,
                2048
              ],
              "default": 1024
            },
            "description": "Longest image edge in pixels. A dense QR may require a larger size; unreadable sizes return 422."
          }
        ]
      }
    },
    "/reviews/{review}/delivery.jpeg": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Download approved artwork as JPEG",
        "description": "Requires reviews:export. Assigned reviewer or owning agency only, with current membership, active key, unchanged content and a valid approval. The complete approved composition is preserved. Artwork is generated outside SQL locks, then access and the approval revision are checked again before counting or releasing bytes. Renderer changes that alter the approved SVG require a new approval.",
        "operationId": "agency_deliver_review_jpeg",
        "responses": {
          "200": {
            "description": "Approved artwork. Private, no-store. Counted separately from ordinary QR exports.",
            "content": {
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "review",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "size",
            "in": "query",
            "schema": {
              "type": "integer",
              "enum": [
                512,
                1024,
                2048
              ],
              "default": 1024
            },
            "description": "Longest image edge in pixels. A dense QR may require a larger size; unreadable sizes return 422."
          }
        ]
      }
    },
    "/transfers": {
      "get": {
        "summary": "List my ownership transfer requests",
        "description": "Requires transfers:read. A company key is restricted to that company; location-bound keys are refused. Summary figures reflect the original request, before the transfer.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated participant requests",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OwnershipTransfer"
                      }
                    },
                    "meta": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or revoked key."
          },
          "403": {
            "description": "Missing permission or invalid account."
          },
          "404": {
            "description": "Not found or outside this key and participant boundary."
          },
          "409": {
            "description": "Content, ownership, revision or capacity changed; reload and prepare a new request if necessary. Do not automatically replay writes."
          },
          "410": {
            "description": "Resolved or expired request."
          },
          "422": {
            "description": "Invalid input, restricted recipient, insufficient quota or technical bound."
          },
          "429": {
            "description": "Rate limit; respect Retry-After."
          }
        },
        "operationId": "get_transfers"
      },
      "post": {
        "summary": "Prepare a company ownership transfer",
        "description": "Requires transfers:write and the current company owner. The recipient must already be an unrestricted team member with verified email, 2FA, company/API/dynamic access and enough global slots. API preparation does not confirm the source owner: both accounts must confirm in the authenticated UI with recent password + 2FA. No API acceptance endpoint. Idempotent request_id; one pending request per company, 50 per source owner, expiry 7 days. Technical limit: 10,000 codes including the bin. Entire company, not personal accounts or subscriptions. Old keys/invitations/reviews are revoked; queued notifications are blocked and preferences disabled. Stateless image usage stays charged to its original payer. Internal applications and archived clients are excluded.",
        "security": [
          {
            "bearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "company_id",
                  "to_user_id",
                  "request_id",
                  "consent"
                ],
                "properties": {
                  "company_id": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "to_user_id": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "request_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "consent": {
                    "type": "boolean",
                    "const": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Prepared request; does not change ownership.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "revision": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "status": {
                          "type": "string"
                        },
                        "source_confirmed": {
                          "type": "boolean",
                          "const": false
                        },
                        "acceptance_url": {
                          "type": "string",
                          "format": "uri"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or revoked key."
          },
          "403": {
            "description": "Missing permission or invalid account."
          },
          "404": {
            "description": "Not found or outside this key and participant boundary."
          },
          "409": {
            "description": "Content, ownership, revision or capacity changed; reload and prepare a new request if necessary. Do not automatically replay writes."
          },
          "410": {
            "description": "Resolved or expired request."
          },
          "422": {
            "description": "Invalid input, restricted recipient, insufficient quota or technical bound."
          },
          "429": {
            "description": "Rate limit; respect Retry-After."
          }
        },
        "operationId": "post_transfers"
      }
    },
    "/transfers/{transfer}": {
      "get": {
        "summary": "Read my transfer request",
        "description": "Requires transfers:read. Source or recipient only; company-bound keys cannot read other companies. A pending recipient must still have unrestricted membership.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "transfer",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Transfer request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/OwnershipTransfer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or revoked key."
          },
          "403": {
            "description": "Missing permission or invalid account."
          },
          "404": {
            "description": "Not found or outside this key and participant boundary."
          },
          "409": {
            "description": "Content, ownership, revision or capacity changed; reload and prepare a new request if necessary. Do not automatically replay writes."
          },
          "410": {
            "description": "Resolved or expired request."
          },
          "422": {
            "description": "Invalid input, restricted recipient, insufficient quota or technical bound."
          },
          "429": {
            "description": "Rate limit; respect Retry-After."
          }
        },
        "operationId": "get_transfers_transfer"
      }
    },
    "/transfers/{transfer}/cancel": {
      "post": {
        "summary": "Withdraw my transfer request",
        "description": "Requires transfers:write. Only the source owner who prepared the request can withdraw it. Ownership remains unchanged.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "transfer",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Withdrawn request with id, status and new revision."
          },
          "401": {
            "description": "Invalid or revoked key."
          },
          "403": {
            "description": "Missing permission or invalid account."
          },
          "404": {
            "description": "Not found or outside this key and participant boundary."
          },
          "409": {
            "description": "Content, ownership, revision or capacity changed; reload and prepare a new request if necessary. Do not automatically replay writes."
          },
          "410": {
            "description": "Resolved or expired request."
          },
          "422": {
            "description": "Invalid input, restricted recipient, insufficient quota or technical bound."
          },
          "429": {
            "description": "Rate limit; respect Retry-After."
          }
        },
        "operationId": "post_transfers_transfer_cancel"
      }
    },
    "/collection-reviews": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "List my collection approvals.",
        "description": "Required scope: `collection_reviews:read`. Company-bound key without location restriction; only the agency owner or assigned unrestricted member. One decision covers all 1–25 non-trashed QR codes in the campaign, including artwork, targets and published content. A changed set, content, ownership, domain or revoked access prevents delivery. Reviews expire after 14 days; encrypted artwork is purged 30 days after expiry. Creation request_id is idempotent and does not reopen a decision.",
        "operationId": "collection_review_get__collection-reviews",
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": []
      },
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Request approval of a frozen complete campaign.",
        "description": "Required scope: `collection_reviews:write`. Company-bound key without location restriction; only the agency owner or assigned unrestricted member. One decision covers all 1–25 non-trashed QR codes in the campaign, including artwork, targets and published content. A changed set, content, ownership, domain or revoked access prevents delivery. Reviews expire after 14 days; encrypted artwork is purged 30 days after expiry. Creation request_id is idempotent and does not reopen a decision.",
        "operationId": "collection_review_post__collection-reviews",
        "responses": {
          "201": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CollectionReviewCreate"
              }
            }
          }
        }
      }
    },
    "/collection-reviews/{review}": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Read an assigned unchanged collection approval.",
        "description": "Required scope: `collection_reviews:read`. Company-bound key without location restriction; only the agency owner or assigned unrestricted member. One decision covers all 1–25 non-trashed QR codes in the campaign, including artwork, targets and published content. A changed set, content, ownership, domain or revoked access prevents delivery. Reviews expire after 14 days; encrypted artwork is purged 30 days after expiry. Creation request_id is idempotent and does not reopen a decision.",
        "operationId": "collection_review_get__collection-reviews_review",
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "review",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/collection-reviews/{review}/decision": {
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Explicit decision by the assigned reviewer for the whole collection.",
        "description": "Required scope: `collection_reviews:decide`. Company-bound key without location restriction; only the agency owner or assigned unrestricted member. One decision covers all 1–25 non-trashed QR codes in the campaign, including artwork, targets and published content. A changed set, content, ownership, domain or revoked access prevents delivery. Reviews expire after 14 days; encrypted artwork is purged 30 days after expiry. Creation request_id is idempotent and does not reopen a decision.",
        "operationId": "collection_review_post__collection-reviews_review_decision",
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "review",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CollectionReviewDecision"
              }
            }
          }
        }
      }
    },
    "/collection-reviews/{review}/revoke": {
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Owner revokes approval, including after a content change.",
        "description": "Required scope: `collection_reviews:write`. Company-bound key without location restriction; only the agency owner or assigned unrestricted member. One decision covers all 1–25 non-trashed QR codes in the campaign, including artwork, targets and published content. A changed set, content, ownership, domain or revoked access prevents delivery. Reviews expire after 14 days; encrypted artwork is purged 30 days after expiry. Creation request_id is idempotent and does not reopen a decision.",
        "operationId": "collection_review_post__collection-reviews_review_revoke",
        "responses": {
          "204": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "review",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/collection-reviews/{review}/archive": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Download an approved ZIP of exact SVGs or complete PNG/JPEG compositions.",
        "description": "Required scope: `collection_reviews:export`. Company-bound key without location restriction; only the agency owner or assigned unrestricted member. One decision covers all 1–25 non-trashed QR codes in the campaign, including artwork, targets and published content. A changed set, content, ownership, domain or revoked access prevents delivery. Reviews expire after 14 days; encrypted artwork is purged 30 days after expiry. Creation request_id is idempotent and does not reopen a decision. Maximum 32 MiB uncompressed archive. Unknown customisation fields are refused. Every right, key, revision and rendered artwork is checked again after rendering; a failed check discards the entire archive. Counts one delivery, separately from QR downloads and image-render quotas. 3 archive requests per minute in the native UI; API also has account rate limits.",
        "operationId": "collection_review_get__collection-reviews_review_archive",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "review",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "svg",
                "png",
                "jpeg"
              ]
            }
          },
          {
            "name": "size",
            "in": "query",
            "description": "PNG/JPEG only, and only if readable for every QR. SVG rejects size. The web view lists the common readable sizes.",
            "schema": {
              "type": "integer",
              "enum": [
                512,
                1024,
                2048
              ],
              "default": 1024
            }
          }
        ]
      }
    },
    "/collection-changes": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "List bulk operations in my workspace and location.",
        "description": "Current actor must have bulk entitlement and edit rights. Explicit scope collection_changes:read for reading, collection_changes:write for preparation and decisions. Kit changes also require codes:style, web targets codes:update, schedules codes:state, campaign assignment campaigns:write; undo additionally requires collection_changes:undo. Location-bound keys are accepted only for their own accessible QR codes and the same location context. Domain changes are refused; existing domains and printed short links stay unchanged. No QR creation, download or opening is counted.",
        "operationId": "mass_get__collection-changes",
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ]
      },
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Prepare a frozen preview; changes are not applied.",
        "description": "Current actor must have bulk entitlement and edit rights. Explicit scope collection_changes:read for reading, collection_changes:write for preparation and decisions. Kit changes also require codes:style, web targets codes:update, schedules codes:state, campaign assignment campaigns:write; undo additionally requires collection_changes:undo. Location-bound keys are accepted only for their own accessible QR codes and the same location context. Domain changes are refused; existing domains and printed short links stay unchanged. No QR creation, download or opening is counted. Maximum 100 unique identifiers. One or more operations, only 10 pending previews and 1000 retained records per payer. Preview expires in 30 minutes. Same request_id and equivalent input return the same record, including reordered fields/IDs and integer encoding. Static content cannot be changed; destination updates support dynamic URL codes only. Style, campaign and schedule values are validated before any mutation. Unknown fields are refused. The receipt has no private before/after values.",
        "operationId": "mass_post__collection-changes",
        "responses": {
          "201": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CollectionChangeCreate"
              }
            }
          }
        }
      }
    },
    "/collection-changes/{operation}": {
      "get": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Read frozen before/after values and explicit conflicts, eight QR codes per page.",
        "description": "Current actor must have bulk entitlement and edit rights. Explicit scope collection_changes:read for reading, collection_changes:write for preparation and decisions. Kit changes also require codes:style, web targets codes:update, schedules codes:state, campaign assignment campaigns:write; undo additionally requires collection_changes:undo. Location-bound keys are accepted only for their own accessible QR codes and the same location context. Domain changes are refused; existing domains and printed short links stay unchanged. No QR creation, download or opening is counted. Requires read scope; response includes items.before/items.after, conflicting_code_ids and kit_changed. It never applies the operation. Previews on the website are regenerated only when their SVG hashes match the frozen proposal. Retention: operations and encrypted snapshots deleted after 30 days by bounded pruning.",
        "operationId": "mass_get__collection-changes_operation",
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "operation",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ]
      }
    },
    "/collection-changes/{operation}/decision": {
      "post": {
        "tags": [
          "Agency and domains"
        ],
        "summary": "Apply atomically, undo eligible QR codes, or withdraw preview.",
        "description": "Current actor must have bulk entitlement and edit rights. Explicit scope collection_changes:read for reading, collection_changes:write for preparation and decisions. Kit changes also require codes:style, web targets codes:update, schedules codes:state, campaign assignment campaigns:write; undo additionally requires collection_changes:undo. Location-bound keys are accepted only for their own accessible QR codes and the same location context. Domain changes are refused; existing domains and printed short links stay unchanged. No QR creation, download or opening is counted. Apply uses the preview revision; one changed QR, kit, entitlement or renderer cancels the whole application. All updates and per-QR history are atomic, with no automatic partial SQL retry. Replaying the original apply revision returns the applied receipt without reapplying. Undo within 24 hours restores only QR codes whose complete revision still matches this operation; later edits are skipped. The whole actor/workspace/location/code access is rechecked. Replaying undo is idempotent. Cancel is available even if a selected QR has been deleted. Rejected SQL conflicts are 409. Review results after any interrupted write.",
        "operationId": "mass_post__collection-changes_operation_decision",
        "responses": {
          "200": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid or revoked key"
          },
          "403": {
            "description": "Insufficient entitlement, scope or role"
          },
          "404": {
            "description": "Resource outside current workspace"
          },
          "409": {
            "description": "Revision or approval changed"
          },
          "410": {
            "description": "Expired or revoked review"
          },
          "422": {
            "description": "Invalid data or quota exceeded"
          },
          "429": {
            "description": "Technical rate or count limit"
          }
        },
        "parameters": [
          {
            "name": "operation",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CollectionChangeDecision"
              }
            }
          }
        }
      }
    },
    "/codes/{code}/access": {
      "get": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current configuration; no password/hash returned",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/QrAccess"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, management rights, real dynamic entitlement or confirmed 2FA"
          },
          "404": {
            "description": "QR unavailable in current workspace/location"
          },
          "409": {
            "description": "Revision, ownership or credentials changed; reload"
          },
          "422": {
            "description": "Static QR, missing/short/overlong secret or unknown fields"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "operationId": "readQrAccess",
        "summary": "Read QR link protection",
        "description": "Explicit access:read; no inheritance from legacy scopes. This controls a shared visitor password, never a user account password. PUT requires owner/admin management, confirmed 2FA and real dynamic entitlement; only dynamic codes. Revision mandatory (null for first configuration), rotated on every change. Access grants are session/host/revision bound for 60 minutes, at most 32 per session. Protected scans, VCF, hosted media, calendars, feedback, restaurant and business-journey public forms are guarded. Password attempts bounded per QR/IP and QR. No opening before successful validation. Transfer clears secret and leaves protected links sealed until the new owner configures access. Changing protection invalidates artwork approvals. Revoked entitlement or expired QR closes access without exposing fallback. API response final authorization is rechecked; no arbitrary public next URL."
      },
      "put": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current configuration; no password/hash returned",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/QrAccess"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, management rights, real dynamic entitlement or confirmed 2FA"
          },
          "404": {
            "description": "QR unavailable in current workspace/location"
          },
          "409": {
            "description": "Revision, ownership or credentials changed; reload"
          },
          "422": {
            "description": "Static QR, missing/short/overlong secret or unknown fields"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "operationId": "configureQrAccess",
        "summary": "Configure QR link protection",
        "description": "Explicit access:write; no inheritance from legacy scopes. This controls a shared visitor password, never a user account password. PUT requires owner/admin management, confirmed 2FA and real dynamic entitlement; only dynamic codes. Revision mandatory (null for first configuration), rotated on every change. Access grants are session/host/revision bound for 60 minutes, at most 32 per session. Protected scans, VCF, hosted media, calendars, feedback, restaurant and business-journey public forms are guarded. Password attempts bounded per QR/IP and QR. No opening before successful validation. Transfer clears secret and leaves protected links sealed until the new owner configures access. Changing protection invalidates artwork approvals. Revoked entitlement or expired QR closes access without exposing fallback. API response final authorization is rechecked; no arbitrary public next URL.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QrAccessUpdate"
              },
              "example": {
                "revision": null,
                "enabled": true,
                "access_password": "example-only-password"
              }
            }
          }
        }
      }
    },
    "/codes/{code}/routing": {
      "get": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/QrRouting"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, edit rights or actual dynamic entitlement"
          },
          "404": {
            "description": "QR inaccessible in this workspace/location"
          },
          "409": {
            "description": "Stale revision or concurrent identity/configuration change; reload"
          },
          "422": {
            "description": "Invalid input, static/non-URL QR or unknown fields"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Read destination rules",
        "operationId": "readQrRouting",
        "description": "Explicit routing:read/write; no legacy scope inheritance. Dynamic URL QR only for writing. Edit role and actual dynamic entitlement checked under lock. First matching rule wins (conditions AND, rules ordered), then optional A/B split, then fallback or main target. Browser preference weights/q=0 respected; supported regional languages reduced to base, English if none. Device is a coarse User-Agent hint, not identity. Time windows start-inclusive/end-exclusive in chosen timezone; overnight days refer to start. Protected QR access is checked before routing/counting. No IP/geolocation/fingerprint stored for allocation. A session/host/QR random seed maintains A/B choice per policy version, max 32 seeds; cookies disabled/new revision may change assignment. Expected weighted allocation, not exact percentages in small samples. Policy encrypted. Changes invalidate client approvals/bulk proposals. Transfer moves configuration, disables routing and rotates revision for new owner review. No network fetch, commerce or conversions inferred. Read requires routing:read; returns targets."
      },
      "put": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized result"
          },
          "403": {
            "description": "Missing explicit scope, edit rights or actual dynamic entitlement"
          },
          "404": {
            "description": "QR inaccessible in this workspace/location"
          },
          "409": {
            "description": "Stale revision or concurrent identity/configuration change; reload"
          },
          "422": {
            "description": "Invalid input, static/non-URL QR or unknown fields"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Save destination rules",
        "operationId": "configureQrRouting",
        "description": "Explicit routing:read/write; no legacy scope inheritance. Dynamic URL QR only for writing. Edit role and actual dynamic entitlement checked under lock. First matching rule wins (conditions AND, rules ordered), then optional A/B split, then fallback or main target. Browser preference weights/q=0 respected; supported regional languages reduced to base, English if none. Device is a coarse User-Agent hint, not identity. Time windows start-inclusive/end-exclusive in chosen timezone; overnight days refer to start. Protected QR access is checked before routing/counting. No IP/geolocation/fingerprint stored for allocation. A session/host/QR random seed maintains A/B choice per policy version, max 32 seeds; cookies disabled/new revision may change assignment. Expected weighted allocation, not exact percentages in small samples. Policy encrypted. Changes invalidate client approvals/bulk proposals. Transfer moves configuration, disables routing and rotates revision for new owner review. No network fetch, commerce or conversions inferred. Write requires routing:write; receipt contains enabled, revision and rule_count, no private targets. Required revision null on first write then exact current value. No QR quota is consumed. Unknown fields rejected; max 10 rules with unique UUIDs. Empty enabled setup rejected. Configuration is replaced atomically. No automatic partial retry.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QrRoutingUpdate"
              },
              "example": {
                "enabled": true,
                "revision": null,
                "timezone": "Europe/Paris",
                "fallback": null,
                "rules": [
                  {
                    "id": "193e4b32-f01d-4187-a235-3d141233f14c",
                    "label": "French phones",
                    "target": "https://example.com/fr",
                    "language": "fr",
                    "device": "mobile",
                    "days": [],
                    "start": null,
                    "end": null
                  }
                ],
                "experiment": {
                  "a": "https://example.com/a",
                  "b": "https://example.com/b",
                  "weight_a": 50
                }
              }
            }
          }
        }
      }
    },
    "/codes/{code}/routing/simulate": {
      "post": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized result"
          },
          "403": {
            "description": "Missing explicit scope, edit rights or actual dynamic entitlement"
          },
          "404": {
            "description": "QR inaccessible in this workspace/location"
          },
          "409": {
            "description": "Stale revision or concurrent identity/configuration change; reload"
          },
          "422": {
            "description": "Invalid input, static/non-URL QR or unknown fields"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Simulate one visit without opening a destination",
        "operationId": "simulateQrRouting",
        "description": "routing:read. Simulates saved configuration only, not an unpublished edit. Explicit ISO 8601 instant with offset, language, device, bucket 0..99. Optional revision guards against a newer configuration. Returns target, kind, choice and policy_revision. No opening, seed or quota is recorded. No URL fetched.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RoutingSimulation"
              },
              "example": {
                "language": "fr",
                "device": "mobile",
                "at": "2026-10-13T13:00:00+02:00",
                "bucket": 25
              }
            }
          }
        }
      }
    },
    "/codes/{code}/routing/results": {
      "get": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized result"
          },
          "403": {
            "description": "Missing explicit scope, edit rights or actual dynamic entitlement"
          },
          "404": {
            "description": "QR inaccessible in this workspace/location"
          },
          "409": {
            "description": "Stale revision or concurrent identity/configuration change; reload"
          },
          "422": {
            "description": "Invalid input, static/non-URL QR or unknown fields"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Read routing openings for the current policy version",
        "operationId": "readQrRoutingResults",
        "description": "routing:read. Latest 30 days, current policy revision only. Returns revision/days/rows; each row has kind, choice, openings, bots, human_openings. Likely bots separated using User-Agent; prefetch not counted. Openings are not unique visitors, sales or confirmed conversions. Existing historical openings remain in global statistics; no previous test winner or statistical significance asserted."
      }
    },
    "/codes/{code}/conversion-settings": {
      "get": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ConversionSettings"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, role or actual API/dynamic entitlement"
          },
          "404": {
            "description": "Code/token inaccessible in this workspace/location"
          },
          "409": {
            "description": "Revision changed or external event conflicts with another payload/QR; reload"
          },
          "410": {
            "description": "Attribution token expired/revoked or policy/QR no longer active"
          },
          "422": {
            "description": "Unknown/structured fields, malformed token/event/kind, false confirmation, non-URL QR or query parameter collision"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Read action attribution settings",
        "operationId": "readConversionSettings",
        "description": "Explicit conversions:read/configure/write; no legacy scope inheritance. Personal/company/location isolation, current permissions checked under lock and before API response delivery. Confirmation from an authenticated client application, not an independent Stripe payment verification. No sales amounts or visitor/order identity accepted. Scans never become conversions automatically. A committed write can survive a later key revocation that rejects the response; retry unchanged event/visit with an authorized key. conversions:read. No token or merchant identifier returned."
      },
      "put": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized result"
          },
          "403": {
            "description": "Missing explicit scope, role or actual API/dynamic entitlement"
          },
          "404": {
            "description": "Code/token inaccessible in this workspace/location"
          },
          "409": {
            "description": "Revision changed or external event conflicts with another payload/QR; reload"
          },
          "410": {
            "description": "Attribution token expired/revoked or policy/QR no longer active"
          },
          "422": {
            "description": "Unknown/structured fields, malformed token/event/kind, false confirmation, non-URL QR or query parameter collision"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Configure action attribution",
        "operationId": "configureConversionSettings",
        "description": "Explicit conversions:read/configure/write; no legacy scope inheritance. Personal/company/location isolation, current permissions checked under lock and before API response delivery. Confirmation from an authenticated client application, not an independent Stripe payment verification. No sales amounts or visitor/order identity accepted. Scans never become conversions automatically. A committed write can survive a later key revocation that rejects the response; retry unchanged event/visit with an authorized key. conversions:configure. Edit role and active API/dynamic entitlement required. Dynamic URL code only. Explicit enable, parameter 1..32 characters, exact revision (null first creation). Detect existing query parameter collisions across current destinations; never replace existing values. Changes invalidate pending tokens and client artwork approvals. Disabled on ownership transfer. No QR/image/download quota consumed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversionSettingsUpdate"
              },
              "example": {
                "revision": null,
                "enabled": true,
                "parameter": "qri"
              }
            }
          }
        }
      }
    },
    "/codes/{code}/conversions": {
      "get": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ConversionReport"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, role or actual API/dynamic entitlement"
          },
          "404": {
            "description": "Code/token inaccessible in this workspace/location"
          },
          "409": {
            "description": "Revision changed or external event conflicts with another payload/QR; reload"
          },
          "410": {
            "description": "Attribution token expired/revoked or policy/QR no longer active"
          },
          "422": {
            "description": "Unknown/structured fields, malformed token/event/kind, false confirmation, non-URL QR or query parameter collision"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Read confirmed actions and attribution cohorts",
        "operationId": "readConfirmedActions",
        "description": "Explicit conversions:read/configure/write; no legacy scope inheritance. Personal/company/location isolation, current permissions checked under lock and before API response delivery. Confirmation from an authenticated client application, not an independent Stripe payment verification. No sales amounts or visitor/order identity accepted. Scans never become conversions automatically. A committed write can survive a later key revocation that rejects the response; retry unchanged event/visit with an authorized key. conversions:read. Visits attributed in the last 30 days form the denominator; distinct converted openings cannot exceed attributed openings. Type counts can exceed converted openings when several actions occur on one visit. Variants cover the current routing revision only; rate uses attributed visits, not all scans or unique people. Probable bots, prefetch and HEAD never receive attribution. No statistical winner inferred."
      },
      "post": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Existing receipt replayed; no new count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ConversionReceipt"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "New confirmed event recorded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ConversionReceipt"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, role or actual API/dynamic entitlement"
          },
          "404": {
            "description": "Code/token inaccessible in this workspace/location"
          },
          "409": {
            "description": "Revision changed or external event conflicts with another payload/QR; reload"
          },
          "410": {
            "description": "Attribution token expired/revoked or policy/QR no longer active"
          },
          "422": {
            "description": "Unknown/structured fields, malformed token/event/kind, false confirmation, non-URL QR or query parameter collision"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Confirm a real client action for an attributed visit",
        "operationId": "confirmClientAction",
        "description": "Explicit conversions:read/configure/write; no legacy scope inheritance. Personal/company/location isolation, current permissions checked under lock and before API response delivery. Confirmation from an authenticated client application, not an independent Stripe payment verification. No sales amounts or visitor/order identity accepted. Scans never become conversions automatically. A committed write can survive a later key revocation that rejects the response; retry unchanged event/visit with an authorized key. conversions:write. Token issued only after a real GET opening and actual API entitlement, bound to QR/workspace/visit/tracking revision/route choice. Token lasts 24 hours; 10,000 active tokens per QR. Above limit or later query collision, redirect still works without a new attribution. One action of each type per visit. Stable external_id unique per workspace (company namespace survives transfer); same payload returns original receipt, different token/type/QR is 409. No plaintext token/event ID stored; HMAC deduplication supports configured previous application keys. A previously confirmed receipt can replay after token expiry/pruning without creating a new event. Transfer revokes pending tokens and preserves historical confirmations. Acting user/key recorded. No public confirmation endpoint and no API key in browser code.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversionConfirmation"
              },
              "example": {
                "token": "0000000000000000000000000000000000000000000000000000000000000000",
                "external_id": "purchase:example-001",
                "kind": "purchase",
                "confirmed": true
              }
            }
          }
        }
      }
    },
    "/codes/{code}/conversions.csv": {
      "get": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Private aggregate CSV",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, role or actual API/dynamic entitlement"
          },
          "404": {
            "description": "Code/token inaccessible in this workspace/location"
          },
          "409": {
            "description": "Revision changed or external event conflicts with another payload/QR; reload"
          },
          "410": {
            "description": "Attribution token expired/revoked or policy/QR no longer active"
          },
          "422": {
            "description": "Unknown/structured fields, malformed token/event/kind, false confirmation, non-URL QR or query parameter collision"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Download aggregate confirmed-action cohorts as CSV",
        "operationId": "exportConfirmedActionsCsv",
        "description": "Explicit conversions:read, same workspace/location isolation and final role/revision/key checks. UTF-8 BOM CSV, aggregate type and current-variant counts/rates only; no visitor token, merchant identifier or original payload. Formula-sensitive labels are escaped. No QR download/image quota counted. Five exports per minute plus global API limits. CSV generation is bounded by the small aggregate dataset, never a raw visit log."
      }
    },
    "/codes/{code}/pass": {
      "get": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PassState"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Explicit scope or current role/actual entitlement missing"
          },
          "404": {
            "description": "Inaccessible QR/workspace/location"
          },
          "409": {
            "description": "Stale pass/page, already used by another request, terminal right or SQL conflict"
          },
          "410": {
            "description": "Not currently usable: paused, unpublished, expired, upcoming, disabled or revoked"
          },
          "422": {
            "description": "Invalid or unknown input; requires dynamic page/pass QR"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Read ticket/coupon state",
        "operationId": "readPass",
        "description": "passes:read. No legacy scope inheritance. Current account, membership, location, key and ownership revision are checked under lock; API response access is rechecked. Each pass is a normal QR slot. Opening the public page or GET never consumes a right. No payment, recipient identity, automated mail or offline confirmation is accepted. A used/revoked right is never reset, including trash restoration. Configuration/revocation requires owner/admin and confirmed 2FA; redemption allows editor. A location-bound key can read/redeem only its accessible QR, but cannot change the owner/admin configuration. Transfer pauses unused rights and preserves receipts. Never expose a key in public browser code."
      },
      "put": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized result"
          },
          "403": {
            "description": "Explicit scope or current role/actual entitlement missing"
          },
          "404": {
            "description": "Inaccessible QR/workspace/location"
          },
          "409": {
            "description": "Stale pass/page, already used by another request, terminal right or SQL conflict"
          },
          "410": {
            "description": "Not currently usable: paused, unpublished, expired, upcoming, disabled or revoked"
          },
          "422": {
            "description": "Invalid or unknown input; requires dynamic page/pass QR"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Configure ticket/coupon entitlement",
        "operationId": "configurePass",
        "description": "passes:configure. No legacy scope inheritance. Current account, membership, location, key and ownership revision are checked under lock; API response access is rechecked. Each pass is a normal QR slot. Opening the public page or GET never consumes a right. No payment, recipient identity, automated mail or offline confirmation is accepted. A used/revoked right is never reset, including trash restoration. Configuration/revocation requires owner/admin and confirmed 2FA; redemption allows editor. A location-bound key can read/redeem only its accessible QR, but cannot change the owner/admin configuration. Transfer pauses unused rights and preserves receipts. Never expose a key in public browser code.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PassConfiguration"
              }
            }
          }
        }
      }
    },
    "/codes/{code}/pass/revoke": {
      "post": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized result"
          },
          "403": {
            "description": "Explicit scope or current role/actual entitlement missing"
          },
          "404": {
            "description": "Inaccessible QR/workspace/location"
          },
          "409": {
            "description": "Stale pass/page, already used by another request, terminal right or SQL conflict"
          },
          "410": {
            "description": "Not currently usable: paused, unpublished, expired, upcoming, disabled or revoked"
          },
          "422": {
            "description": "Invalid or unknown input; requires dynamic page/pass QR"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Permanently revoke an unused entitlement",
        "operationId": "revokePass",
        "description": "passes:configure. No legacy scope inheritance. Current account, membership, location, key and ownership revision are checked under lock; API response access is rechecked. Each pass is a normal QR slot. Opening the public page or GET never consumes a right. No payment, recipient identity, automated mail or offline confirmation is accepted. A used/revoked right is never reset, including trash restoration. Configuration/revocation requires owner/admin and confirmed 2FA; redemption allows editor. A location-bound key can read/redeem only its accessible QR, but cannot change the owner/admin configuration. Transfer pauses unused rights and preserves receipts. Never expose a key in public browser code.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PassRevoke"
              }
            }
          }
        }
      }
    },
    "/codes/{code}/pass/redeem": {
      "post": {
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Exact request replay; original receipt without a second use",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PassReceipt"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "New redemption receipt",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PassReceipt"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Explicit scope or current role/actual entitlement missing"
          },
          "404": {
            "description": "Inaccessible QR/workspace/location"
          },
          "409": {
            "description": "Stale pass/page, already used by another request, terminal right or SQL conflict"
          },
          "410": {
            "description": "Not currently usable: paused, unpublished, expired, upcoming, disabled or revoked"
          },
          "422": {
            "description": "Invalid or unknown input; requires dynamic page/pass QR"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "Redeem a ticket or coupon once",
        "operationId": "redeemPass",
        "description": "passes:redeem. No legacy scope inheritance. Current account, membership, location, key and ownership revision are checked under lock; API response access is rechecked. Each pass is a normal QR slot. Opening the public page or GET never consumes a right. No payment, recipient identity, automated mail or offline confirmation is accepted. A used/revoked right is never reset, including trash restoration. Configuration/revocation requires owner/admin and confirmed 2FA; redemption allows editor. A location-bound key can read/redeem only its accessible QR, but cannot change the owner/admin configuration. Transfer pauses unused rights and preserves receipts. Never expose a key in public browser code. Send the revision and page_version read during the check, an explicit confirmed=true and stable UUID request_id. Identical retries on this same QR return the original receipt. Another request ID or payload after use is 409. At expiry equality the right is invalid. SQL unique constraints and payer/company/QR/pass locks prevent concurrent double use; never partially retry a failed transaction.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PassRedemption"
              }
            }
          }
        }
      }
    },
    "/passes": {
      "get": {
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100000
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "maxItems": 50,
                      "items": {
                        "$ref": "#/components/schemas/PassListItem"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "current_page": {
                          "type": "integer"
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "per_page": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Explicit scope or current role/actual entitlement missing"
          },
          "404": {
            "description": "Inaccessible QR/workspace/location"
          },
          "409": {
            "description": "Stale pass/page, already used by another request, terminal right or SQL conflict"
          },
          "410": {
            "description": "Not currently usable: paused, unpublished, expired, upcoming, disabled or revoked"
          },
          "422": {
            "description": "Invalid or unknown input; requires dynamic page/pass QR"
          },
          "429": {
            "description": "Rate limit; respect Retry-After"
          }
        },
        "summary": "List accessible tickets and coupons",
        "operationId": "listPasses",
        "description": "passes:read. No legacy scope inheritance. Current account, membership, location, key and ownership revision are checked under lock; API response access is rechecked. Each pass is a normal QR slot. Opening the public page or GET never consumes a right. No payment, recipient identity, automated mail or offline confirmation is accepted. A used/revoked right is never reset, including trash restoration. Configuration/revocation requires owner/admin and confirmed 2FA; redemption allows editor. A location-bound key can read/redeem only its accessible QR, but cannot change the owner/admin configuration. Transfer pauses unused rights and preserves receipts. Never expose a key in public browser code. Search internal labels and optionally filter an owned campaign. Location selection remains bound to the authenticated key. Pagination totals, ordered IDs and all resources are rechecked together under the current workspace lock before delivery; an empty page cannot expose an old total after membership/location changes. GET never redeems."
      }
    },
    "/codes/{code}/asset": {
      "get": {
        "operationId": "readAsset",
        "tags": [
          "Inventory QR/NFC"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "assets:read. Current actor/payer/company/location/key/ownership revision read under lock. Roles and API scopes never increase commercial rights. Configure requires owner/admin with 2FA; interventions allow native editor. Location-bound keys may read/check their site, but cannot configure. Transfer disables inventory, rotates revisions and preserves historical operators. No automatic email or hardware programming. Never expose API keys to visitors.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Asset"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over 32768 bytes; accommodates escaped valid Unicode notes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "configureAsset",
        "tags": [
          "Inventory QR/NFC"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "assets:configure. Current actor/payer/company/location/key/ownership revision read under lock. Roles and API scopes never increase commercial rights. Configure requires owner/admin with 2FA; interventions allow native editor. Location-bound keys may read/check their site, but cannot configure. Transfer disables inventory, rotates revisions and preserves historical operators. No automatic email or hardware programming. Never expose API keys to visitors.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Revision"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over 32768 bytes; accommodates escaped valid Unicode notes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssetConfiguration"
              }
            }
          }
        }
      }
    },
    "/codes/{code}/asset/checks": {
      "get": {
        "operationId": "readAssetChecks",
        "tags": [
          "Inventory QR/NFC"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100000
            }
          }
        ],
        "description": "assets:history. Current actor/payer/company/location/key/ownership revision read under lock. Roles and API scopes never increase commercial rights. Configure requires owner/admin with 2FA; interventions allow native editor. Location-bound keys may read/check their site, but cannot configure. Transfer disables inventory, rotates revisions and preserves historical operators. No automatic email or hardware programming. Never expose API keys to visitors.",
        "responses": {
          "200": {
            "description": "Paginated internal reports, latest server time then private numeric sequence",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "maxItems": 20,
                      "items": {
                        "$ref": "#/components/schemas/AssetCheck"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "current_page",
                        "last_page",
                        "total"
                      ],
                      "properties": {
                        "current_page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "last_page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over 32768 bytes; accommodates escaped valid Unicode notes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "recordAssetCheck",
        "tags": [
          "Inventory QR/NFC"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "assets:checks. Current actor/payer/company/location/key/ownership revision read under lock. Roles and API scopes never increase commercial rights. Configure requires owner/admin with 2FA; interventions allow native editor. Location-bound keys may read/check their site, but cannot configure. Transfer disables inventory, rotates revisions and preserves historical operators. No automatic email or hardware programming. Never expose API keys to visitors.",
        "responses": {
          "200": {
            "description": "Exact replay of original receipt",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AssetCheckReceipt"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AssetCheckReceipt"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over 32768 bytes; accommodates escaped valid Unicode notes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssetCheckInput"
              }
            }
          }
        }
      }
    },
    "/assets": {
      "get": {
        "operationId": "listAssets",
        "tags": [
          "Inventory QR/NFC"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "equipment",
                "product"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100000
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "description": "assets:read. Current actor/payer/company/location/key/ownership revision read under lock. Roles and API scopes never increase commercial rights. Configure requires owner/admin with 2FA; interventions allow native editor. Location-bound keys may read/check their site, but cannot configure. Transfer disables inventory, rotates revisions and preserves historical operators. No automatic email or hardware programming. Never expose API keys to visitors.",
        "responses": {
          "200": {
            "description": "Owned dynamic product pages including objects awaiting configuration. Search label substring or exact normalized reference. Filter configured equipment/product. Recheck total, ordered IDs and revisions together, including empty pages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "maxItems": 50,
                      "items": {
                        "$ref": "#/components/schemas/Asset"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "current_page",
                        "last_page",
                        "total",
                        "per_page"
                      ],
                      "properties": {
                        "current_page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "last_page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "per_page": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 50
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over 32768 bytes; accommodates escaped valid Unicode notes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/assets.csv": {
      "get": {
        "operationId": "exportAssetReferences",
        "tags": [
          "Inventory QR/NFC"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "equipment",
                "product"
              ]
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "description": "Explicit assets:export, independent from assets:read. CSV UTF-8 with BOM, comma separator, CRLF. At most 500 configured enabled objects, filtered in current company/location; over-limit is 422, never a truncated file. Columns: code_id,label,kind,reference,sku,url,next_due_utc,timezone,page_published. Serial, lot, shelf, notes and historical operators are excluded. Spreadsheet formulas are escaped with a leading apostrophe, including prefixed Unicode whitespace. Ordered IDs and all revisions are rechecked under workspace lock after the complete file is prepared; 409 never releases a partial file. Named budget 5/minute per actor, shared by native/API and multiple keys, independent of metadata reads. Use the same short URL for physical NFC/QR; no remote tag writing or e-ink supplier compatibility is promised. A metadata CSV is not counted as an individual QR image download.",
        "responses": {
          "200": {
            "description": "Reference CSV",
            "headers": {
              "X-QRissimo-Count": {
                "schema": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 500
                }
              }
            },
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over 32768 bytes; accommodates escaped valid Unicode notes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/codes/{code}/commerce": {
      "get": {
        "operationId": "readCommerceFeed",
        "tags": [
          "Price and stock"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "commerce:read. Explicit scope; no qr:* inheritance. Current actor/payer/company/key/location/ownership revision rechecked under lock. No client value grants rights. No SMTP or network fetch to a client URL.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CommerceFeed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "configureCommerceFeed",
        "tags": [
          "Price and stock"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "commerce:configure. Explicit scope; no qr:* inheritance. Current actor/payer/company/key/location/ownership revision rechecked under lock. No client value grants rights. No SMTP or network fetch to a client URL.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Revision"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommerceConfiguration"
              }
            }
          }
        }
      },
      "post": {
        "operationId": "updateCommerceFeed",
        "tags": [
          "Price and stock"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "commerce:write. Explicit scope; no qr:* inheritance. Current actor/payer/company/key/location/ownership revision rechecked under lock. No client value grants rights. No SMTP or network fetch to a client URL.",
        "responses": {
          "200": {
            "description": "Exact retained replay",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CommerceReceipt"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CommerceReceipt"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommerceObservation"
              }
            }
          }
        }
      }
    },
    "/commerce/updates": {
      "post": {
        "operationId": "updateCommerceFeedsBatch",
        "tags": [
          "Price and stock"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [],
        "description": "commerce:write. Explicit scope; no qr:* inheritance. Current actor/payer/company/key/location/ownership revision rechecked under lock. No client value grants rights. No SMTP or network fetch to a client URL.",
        "responses": {
          "200": {
            "description": "Input-order receipts, 201 if any new update, 200 if all replayed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "maxItems": 50,
                      "items": {
                        "$ref": "#/components/schemas/CommerceReceipt"
                      }
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Input-order receipts, 201 if any new update, 200 if all replayed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "maxItems": 50,
                      "items": {
                        "$ref": "#/components/schemas/CommerceReceipt"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommerceBatch"
              }
            }
          }
        }
      }
    },
    "/commerce/feeds": {
      "get": {
        "operationId": "listCommerceFeeds",
        "tags": [
          "Price and stock"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100000,
              "default": 1
            }
          }
        ],
        "description": "commerce:read explicit. All owned configured product feeds, including disabled/disconnected/stale. Private last-received values are not a live availability guarantee. Label search follows database collation. No SKU plaintext search. Pagination and row snapshots, ownership, location, source and access rechecked together before delivery.",
        "responses": {
          "200": {
            "description": "Private paginated feeds",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CommerceFeed"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "required": [
                        "current_page",
                        "last_page",
                        "per_page",
                        "total"
                      ],
                      "properties": {
                        "current_page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "last_page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "per_page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/commerce/feeds.csv": {
      "get": {
        "operationId": "exportCommerceFeedsCsv",
        "tags": [
          "Price and stock"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "commerce:export explicit; no commerce:read or qr:* inheritance. Complete owned feed export, maximum 500 rows or 422 with no partial file. Label/campaign filters. Only live values exported: stale/disconnected/disabled/unavailable price, currency, tax_mode and quantity are empty, status/dates retained. Zero remains zero. UTF-8 BOM, comma, CRLF, machine headers, spreadsheet formula mitigation. No token, source key/name, actor or private material detail. Entire file and access/ids/revisions/source/clock rechecked before delivery. Named 5 exports/minute per actor shared across keys and native route; QR image counters unchanged.",
        "responses": {
          "200": {
            "description": "Complete price/stock CSV snapshot",
            "headers": {
              "X-QRissimo-Count": {
                "schema": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 500
                }
              },
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/codes/{code}/monitoring": {
      "get": {
        "operationId": "readDestinationMonitoring",
        "tags": [
          "Destination monitoring"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "monitoring:read explicit, no qr:* inheritance. Re-read payer/actor/company/ownership/location/role/2FA/key access under lock. Source URLs never accepted in this request. Configuration/checks exclude location-bound management keys. Request bodies capped at 4 KiB. No network IO during a read/configuration.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MonitoringData"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Network monitoring operator gate disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "configureDestinationMonitoring",
        "tags": [
          "Destination monitoring"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "monitoring:write explicit, no qr:* inheritance. Re-read payer/actor/company/ownership/location/role/2FA/key access under lock. Source URLs never accepted in this request. Configuration/checks exclude location-bound management keys. Request bodies capped at 4 KiB. No network IO during a read/configuration.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Revision"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Network monitoring operator gate disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MonitoringConfiguration"
              }
            }
          }
        }
      }
    },
    "/codes/{code}/monitoring/alerts": {
      "put": {
        "operationId": "configureOwnDestinationAlerts",
        "tags": [
          "Destination monitoring"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "monitoring:alerts explicit, no qr:* inheritance. Re-read payer/actor/company/ownership/location/role/2FA/key access under lock. Source URLs never accepted in this request. Configuration/checks exclude location-bound management keys. Request bodies capped at 4 KiB. No network IO during a read/configuration.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Revision"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Network monitoring operator gate disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MonitoringAlertPreference"
              }
            }
          }
        }
      }
    },
    "/codes/{code}/monitoring/check": {
      "post": {
        "operationId": "queueDueDestinationChecks",
        "tags": [
          "Destination monitoring"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "monitoring:check explicit, no qr:* inheritance. Re-read payer/actor/company/ownership/location/role/2FA/key access under lock. Source URLs never accepted in this request. Configuration/checks exclude location-bound management keys. Request bodies capped at 4 KiB. No network IO during a read/configuration. Due targets only, no force/cadence bypass. Named 5 requests/minute per actor across keys/native. Operator network budgets (default 20 global, 2 payer, 2 host per minute) plus persistent exclusive claims. Zero queued is valid when not due/already queued/budget reached.",
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "queued": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 15
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Network monitoring operator gate disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Revision"
              }
            }
          }
        }
      }
    },
    "/codes/{code}/monitoring/incidents/{incident}/acknowledge": {
      "post": {
        "operationId": "acknowledgeDestinationIncident",
        "tags": [
          "Destination monitoring"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "incident",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "description": "monitoring:write explicit, no qr:* inheritance. Re-read payer/actor/company/ownership/location/role/2FA/key access under lock. Source URLs never accepted in this request. Configuration/checks exclude location-bound management keys. Request bodies capped at 4 KiB. No network IO during a read/configuration.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "acknowledged": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Network monitoring operator gate disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MonitoringAcknowledge"
              }
            }
          }
        }
      }
    },
    "/monitoring": {
      "get": {
        "operationId": "listDestinationMonitoring",
        "tags": [
          "Destination monitoring"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100000
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          }
        ],
        "description": "monitoring:read explicit, no qr:* inheritance. Re-read payer/actor/company/ownership/location/role/2FA/key access under lock. Source URLs never accepted in this request. Configuration/checks exclude location-bound management keys. Request bodies capped at 4 KiB. No network IO during a read/configuration.",
        "responses": {
          "200": {
            "description": "Own paginated monitored QR codes, including paused ones",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MonitoringData"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "current_page": {
                          "type": "integer"
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "per_page": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Technical body bound exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Network monitoring operator gate disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/page-publications": {
      "get": {
        "operationId": "listPagesForBatchPublication",
        "tags": [
          "Page batch publication"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "page_publications:read. Explicit scopes; no legacy inheritance. Fresh workspace/actor/payer/role/ownership/location/key checked under lock. 50 pages, 2 MiB aggregate canonical content, 30-minute preview. Campaign selection means all accessible hosted pages; external QR links excluded. Atomic publish, unchanged QR URLs, no automatic email. Private no-store.",
        "parameters": [
          {
            "name": "campaign",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "current_page": {
                          "type": "integer"
                        },
                        "data": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string",
                                "format": "uuid"
                              },
                              "qr_code_id": {
                                "type": "integer"
                              },
                              "label": {
                                "type": "string"
                              },
                              "revision": {
                                "type": "string",
                                "format": "uuid"
                              },
                              "published_version_id": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "format": "uuid"
                              }
                            }
                          }
                        },
                        "last_page": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        },
                        "per_page": {
                          "type": "integer",
                          "const": 50
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, role or real dynamic entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not in this workspace or location",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Source, membership, revision or access changed; no partial publication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Preview expired or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "16 KiB request or 2 MiB aggregate content exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid or incomplete selection/draft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preview/history/write budget exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "preparePagePublication",
        "tags": [
          "Page batch publication"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "page_publications:write + pages:read. Explicit scopes; no legacy inheritance. Fresh workspace/actor/payer/role/ownership/location/key checked under lock. 50 pages, 2 MiB aggregate canonical content, 30-minute preview. Campaign selection means all accessible hosted pages; external QR links excluded. Atomic publish, unchanged QR URLs, no automatic email. Private no-store. batch_uuid selects a completed page import and additionally requires batches:read. It freezes all its accessible page identities.",
        "parameters": [],
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PagePublication"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, role or real dynamic entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not in this workspace or location",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Source, membership, revision or access changed; no partial publication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Preview expired or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "16 KiB request or 2 MiB aggregate content exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid or incomplete selection/draft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preview/history/write budget exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PagePublicationPrepare"
              }
            }
          }
        }
      }
    },
    "/page-publications/{operation}": {
      "get": {
        "operationId": "readPagePublication",
        "tags": [
          "Page batch publication"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "page_publications:read. Explicit scopes; no legacy inheritance. Fresh workspace/actor/payer/role/ownership/location/key checked under lock. 50 pages, 2 MiB aggregate canonical content, 30-minute preview. Campaign selection means all accessible hosted pages; external QR links excluded. Atomic publish, unchanged QR URLs, no automatic email. Private no-store.",
        "parameters": [
          {
            "name": "operation",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PagePublication"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, role or real dynamic entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not in this workspace or location",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Source, membership, revision or access changed; no partial publication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Preview expired or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "16 KiB request or 2 MiB aggregate content exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid or incomplete selection/draft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preview/history/write budget exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/page-publications/{operation}/decision": {
      "post": {
        "operationId": "decidePagePublication",
        "tags": [
          "Page batch publication"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "page_publications:write + pages:publish for publish; page_publications:write for cancel. Explicit scopes; no legacy inheritance. Fresh workspace/actor/payer/role/ownership/location/key checked under lock. 50 pages, 2 MiB aggregate canonical content, 30-minute preview. Campaign selection means all accessible hosted pages; external QR links excluded. Atomic publish, unchanged QR URLs, no automatic email. Private no-store. Replaying the consumed revision/action returns the same receipt without a second version; a later edit is never reverted. Labels/languages omitted without page_publications:read. Cancel discards proposal without unpublishing existing pages.",
        "parameters": [
          {
            "name": "operation",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PagePublication"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, role or real dynamic entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not in this workspace or location",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Source, membership, revision or access changed; no partial publication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Preview expired or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "16 KiB request or 2 MiB aggregate content exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid or incomplete selection/draft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preview/history/write budget exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PagePublicationDecision"
              }
            }
          }
        }
      }
    },
    "/page-publications/{operation}/pages/{page}/preview": {
      "get": {
        "operationId": "previewPageInPublication",
        "tags": [
          "Page batch publication"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "page_publications:read. Explicit scopes; no legacy inheritance. Fresh workspace/actor/payer/role/ownership/location/key checked under lock. 50 pages, 2 MiB aggregate canonical content, 30-minute preview. Campaign selection means all accessible hosted pages; external QR links excluded. Atomic publish, unchanged QR URLs, no automatic email. Private no-store.",
        "parameters": [
          {
            "name": "operation",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "fr",
                "en",
                "it",
                "es",
                "de",
                "pt",
                "ro",
                "ru",
                "uk",
                "nl",
                "pl",
                "cs",
                "sk",
                "hu",
                "el",
                "bg",
                "hr",
                "sr",
                "tr",
                "sv",
                "da",
                "fi",
                "nb"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Private HTML preview, no public scan recorded; access and full set fingerprint rechecked after render.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, role or real dynamic entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not in this workspace or location",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Source, membership, revision or access changed; no partial publication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Preview expired or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "16 KiB request or 2 MiB aggregate content exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid or incomplete selection/draft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preview/history/write budget exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/page-imports": {
      "post": {
        "operationId": "prepareHostedPageImport",
        "tags": [
          "Hosted page import"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [],
        "description": "pages:write + batches:write. Fresh workspace/payer/actor/role/location/key checks. Real batch and dynamic entitlements required. 50 rows, one language per import, new draft and one QR slot per row. No public content change, email, payment or upload formula execution. Existing Batch CSV URL mapping/confirmation routes cannot remap or confirm a page import.",
        "responses": {
          "201": {
            "description": "Success; content snapshots only on the read endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PageImport"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, role or real dynamic entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not in this workspace or location",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Source, membership, revision or access changed; no partial publication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Preview expired or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "File exceeds 1 MiB or request exceeds 1 MiB + 16 KiB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid or incomplete selection/draft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preview/history/write budget exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PageImportPrepare"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PageImportPrepare"
              }
            }
          }
        }
      }
    },
    "/page-imports/{batch}": {
      "get": {
        "operationId": "readHostedPageImport",
        "tags": [
          "Hosted page import"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "batch",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "description": "pages:read + batches:read. Fresh workspace/payer/actor/role/location/key checks. Real batch and dynamic entitlements required. 50 rows, one language per import, new draft and one QR slot per row. No public content change, email, payment or upload formula execution. Existing Batch CSV URL mapping/confirmation routes cannot remap or confirm a page import.",
        "responses": {
          "200": {
            "description": "Success; content snapshots only on the read endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PageImport"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, role or real dynamic entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not in this workspace or location",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Source, membership, revision or access changed; no partial publication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Preview expired or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "File exceeds 1 MiB or request exceeds 1 MiB + 16 KiB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid or incomplete selection/draft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preview/history/write budget exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/page-imports/{batch}/confirm": {
      "post": {
        "operationId": "confirmHostedPageImport",
        "tags": [
          "Hosted page import"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "batch",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "description": "pages:write + batches:write + codes:create. Fresh workspace/payer/actor/role/location/key checks. Real batch and dynamic entitlements required. 50 rows, one language per import, new draft and one QR slot per row. No public content change, email, payment or upload formula execution. Existing Batch CSV URL mapping/confirmation routes cannot remap or confirm a page import. Stable consumed batch revision returns the same receipt, never overwrites later draft edits or restores trashed codes. All rows or none, quota rechecked under lock, no exclude_invalid. Read/confirm drafts expire after 24h; completed receipt remains subject to current access.",
        "responses": {
          "200": {
            "description": "Success; content snapshots only on the read endpoint",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PageImport"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope, role or real dynamic entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not in this workspace or location",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Source, membership, revision or access changed; no partial publication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Preview expired or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "File exceeds 1 MiB or request exceeds 1 MiB + 16 KiB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid or incomplete selection/draft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preview/history/write budget exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PageImportConfirmation"
              }
            }
          }
        }
      }
    },
    "/reports": {
      "get": {
        "operationId": "listMyPdfReports",
        "tags": [
          "PDF reports"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "reports:read. Private no-store, verified account address of the authenticated actor only, no arbitrary recipient/From. Actor-specific encrypted snapshot/PDF; current QR grants, payer, role, ownership and key rechecked under lock. 366 days, 200 detailed rows, 5000 aggregate QR, PDF 2 MiB/35 pages, retention 30 days. Robots separated; declared purchases are not verified Stripe payments. UTC-day limits: creation 10/actor,30/payer,200/global; email 5/actor,20/payer,100/global; 100 retained reports/payer. ",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AnalyticsPdfReport"
                      },
                      "maxItems": 20
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, role or verified active entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Outside actor workspace or permitted locations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed payload, grants, ownership, source or SQL conflict; no retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Expired or revoked report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 4 KiB or aggregate PDF input limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unknown fields or invalid dates; detail above 200 QR or selection above 5000",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily budget or 5 PDF downloads/minute; Retry-After when present",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Isolated PDF renderer unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createMyPdfReport",
        "tags": [
          "PDF reports"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "reports:create + statistics:read. Private no-store, verified account address of the authenticated actor only, no arbitrary recipient/From. Actor-specific encrypted snapshot/PDF; current QR grants, payer, role, ownership and key rechecked under lock. 366 days, 200 detailed rows, 5000 aggregate QR, PDF 2 MiB/35 pages, retention 30 days. Robots separated; declared purchases are not verified Stripe payments. UTC-day limits: creation 10/actor,30/payer,200/global; email 5/actor,20/payer,100/global; 100 retained reports/payer. ",
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AnalyticsPdfReport"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Same idempotent request already rendering",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AnalyticsPdfReport"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, role or verified active entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Outside actor workspace or permitted locations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed payload, grants, ownership, source or SQL conflict; no retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Expired or revoked report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 4 KiB or aggregate PDF input limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unknown fields or invalid dates; detail above 200 QR or selection above 5000",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily budget or 5 PDF downloads/minute; Retry-After when present",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Isolated PDF renderer unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAnalyticsPdfReport"
              }
            }
          }
        }
      }
    },
    "/reports/{report}": {
      "get": {
        "operationId": "getMyPdfReport",
        "tags": [
          "PDF reports"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "reports:read. Private no-store, verified account address of the authenticated actor only, no arbitrary recipient/From. Actor-specific encrypted snapshot/PDF; current QR grants, payer, role, ownership and key rechecked under lock. 366 days, 200 detailed rows, 5000 aggregate QR, PDF 2 MiB/35 pages, retention 30 days. Robots separated; declared purchases are not verified Stripe payments. UTC-day limits: creation 10/actor,30/payer,200/global; email 5/actor,20/payer,100/global; 100 retained reports/payer. ",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "revision": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "rendering",
                            "ready",
                            "failed"
                          ]
                        },
                        "period": {
                          "type": "object",
                          "required": [
                            "from",
                            "to",
                            "timezone"
                          ],
                          "properties": {
                            "from": {
                              "type": "string",
                              "format": "date"
                            },
                            "to": {
                              "type": "string",
                              "format": "date"
                            },
                            "timezone": {
                              "type": "string"
                            }
                          }
                        },
                        "language": {
                          "type": "string",
                          "enum": [
                            "fr",
                            "en",
                            "it",
                            "es",
                            "de",
                            "pt",
                            "ro",
                            "ru",
                            "uk",
                            "nl",
                            "pl",
                            "cs",
                            "sk",
                            "hu",
                            "el",
                            "bg",
                            "hr",
                            "sr",
                            "tr",
                            "sv",
                            "da",
                            "fi",
                            "nb"
                          ]
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "generated_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        },
                        "pdf_bytes": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "maximum": 2097152
                        },
                        "summary": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        },
                        "mail": {
                          "type": "array",
                          "maxItems": 20,
                          "items": {
                            "$ref": "#/components/schemas/ReportEmailReceipt"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, role or verified active entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Outside actor workspace or permitted locations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed payload, grants, ownership, source or SQL conflict; no retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Expired or revoked report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 4 KiB or aggregate PDF input limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unknown fields or invalid dates; detail above 200 QR or selection above 5000",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily budget or 5 PDF downloads/minute; Retry-After when present",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Isolated PDF renderer unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "report",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/reports/{report}/pdf": {
      "get": {
        "operationId": "downloadMyPdfReport",
        "tags": [
          "PDF reports"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "reports:read. Private no-store, verified account address of the authenticated actor only, no arbitrary recipient/From. Actor-specific encrypted snapshot/PDF; current QR grants, payer, role, ownership and key rechecked under lock. 366 days, 200 detailed rows, 5000 aggregate QR, PDF 2 MiB/35 pages, retention 30 days. Robots separated; declared purchases are not verified Stripe payments. UTC-day limits: creation 10/actor,30/payer,200/global; email 5/actor,20/payer,100/global; 100 retained reports/payer. ",
        "responses": {
          "200": {
            "description": "Exact prepared PDF, no QR download increment",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, role or verified active entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Outside actor workspace or permitted locations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed payload, grants, ownership, source or SQL conflict; no retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Expired or revoked report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 4 KiB or aggregate PDF input limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unknown fields or invalid dates; detail above 200 QR or selection above 5000",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily budget or 5 PDF downloads/minute; Retry-After when present",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Isolated PDF renderer unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "report",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/reports/{report}/email": {
      "post": {
        "operationId": "emailMyPdfReport",
        "tags": [
          "PDF reports"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "reports:email. Private no-store, verified account address of the authenticated actor only, no arbitrary recipient/From. Actor-specific encrypted snapshot/PDF; current QR grants, payer, role, ownership and key rechecked under lock. 366 days, 200 detailed rows, 5000 aggregate QR, PDF 2 MiB/35 pages, retention 30 days. Robots separated; declared purchases are not verified Stripe payments. UTC-day limits: creation 10/actor,30/payer,200/global; email 5/actor,20/payer,100/global; 100 retained reports/payer. ",
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ReportEmailReceipt"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, role or verified active entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Outside actor workspace or permitted locations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed payload, grants, ownership, source or SQL conflict; no retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Expired or revoked report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 4 KiB or aggregate PDF input limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unknown fields or invalid dates; detail above 200 QR or selection above 5000",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily budget or 5 PDF downloads/minute; Retry-After when present",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Isolated PDF renderer unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReportEmailRequest"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "report",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/reports/schedule": {
      "get": {
        "operationId": "getMyReportSchedule",
        "tags": [
          "PDF reports"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "reports:schedule. Private no-store, verified account address of the authenticated actor only, no arbitrary recipient/From. Actor-specific encrypted snapshot/PDF; current QR grants, payer, role, ownership and key rechecked under lock. 366 days, 200 detailed rows, 5000 aggregate QR, PDF 2 MiB/35 pages, retention 30 days. Robots separated; declared purchases are not verified Stripe payments. UTC-day limits: creation 10/actor,30/payer,200/global; email 5/actor,20/payer,100/global; 100 retained reports/payer. ",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ReportSchedule"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, role or verified active entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Outside actor workspace or permitted locations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed payload, grants, ownership, source or SQL conflict; no retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Expired or revoked report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 4 KiB or aggregate PDF input limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unknown fields or invalid dates; detail above 200 QR or selection above 5000",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily budget or 5 PDF downloads/minute; Retry-After when present",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Isolated PDF renderer unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "configureMyReportSchedule",
        "tags": [
          "PDF reports"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "reports:schedule. Private no-store, verified account address of the authenticated actor only, no arbitrary recipient/From. Actor-specific encrypted snapshot/PDF; current QR grants, payer, role, ownership and key rechecked under lock. 366 days, 200 detailed rows, 5000 aggregate QR, PDF 2 MiB/35 pages, retention 30 days. Robots separated; declared purchases are not verified Stripe payments. UTC-day limits: creation 10/actor,30/payer,200/global; email 5/actor,20/payer,100/global; 100 retained reports/payer. Enabling additionally requires reports:create + reports:email + statistics:read. One opt-in per actor/workspace/location, Monday or first of month at 08:00 local, previous complete period; latest missed period only. Revision required, queued email blocked on changed preference. Origin key ID survives deletion and is rechecked. Jobs one attempt, uncertain SMTP never auto resent.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "revision": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "uuid"
                        },
                        "enabled": {
                          "type": "boolean"
                        },
                        "next_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing explicit scope, role or verified active entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Outside actor workspace or permitted locations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed payload, grants, ownership, source or SQL conflict; no retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Expired or revoked report",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body above 4 KiB or aggregate PDF input limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unknown fields or invalid dates; detail above 200 QR or selection above 5000",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily budget or 5 PDF downloads/minute; Retry-After when present",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Isolated PDF renderer unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfigureReportSchedule"
              }
            }
          }
        }
      }
    },
    "/portability/export": {
      "post": {
        "operationId": "exportPortableArchive",
        "tags": [
          "Portability"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "portability:export + codes:read. Explicit scopes, owner/admin, verified email and 2FA. Current actor/payer/company ownership/location/key rechecked under locking reads. 25 QR/10 files/10 MiB uncompressed archive, manifest 2 MiB. Native export also after subscription ends, without reactivating resources; API keeps normal active entitlement. ZIP path/duplicate/bomb protections, no disk extraction and no network. Native selection is explicit; API codes/campaign exclusive or all accessible <=25. Export requires codes:read, pages:read if pages, history:read when history=true. Restoration requires real batch entitlement + codes:create, pages:write if pages, each new QR uses a place. New dynamic QR paused, pages draft, no old URLs/domains/team/payments/policies/active history restored. Protected Wi-Fi/raw entries excluded and shown in preview. Private no-store, no QR download increment. SQL conflicts 409 without automatic retry; read then explicitly replay same request UUID/revision. Preview encrypted, 24h, five active per payer, purge 30d after expiry. Origin key ID survives deletion; revoke/transfer blocks confirmation. ",
        "responses": {
          "200": {
            "description": "ZIP with manifest.json and referenced files",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing role/2FA/scope or real batch entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Outside boundary or origin missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/payload/source/SQL conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Expired/revoked preparation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Archive size/limits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid ZIP/manifest/selection/content",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preparation or download budget",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Normalizer unavailable or processing bound",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortabilityExport"
              }
            }
          }
        }
      }
    },
    "/portability/imports": {
      "post": {
        "operationId": "preparePortableRestore",
        "tags": [
          "Portability"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "portability:import + codes:create. Explicit scopes, owner/admin, verified email and 2FA. Current actor/payer/company ownership/location/key rechecked under locking reads. 25 QR/10 files/10 MiB uncompressed archive, manifest 2 MiB. Native export also after subscription ends, without reactivating resources; API keeps normal active entitlement. ZIP path/duplicate/bomb protections, no disk extraction and no network. Native selection is explicit; API codes/campaign exclusive or all accessible <=25. Export requires codes:read, pages:read if pages, history:read when history=true. Restoration requires real batch entitlement + codes:create, pages:write if pages, each new QR uses a place. New dynamic QR paused, pages draft, no old URLs/domains/team/payments/policies/active history restored. Protected Wi-Fi/raw entries excluded and shown in preview. Private no-store, no QR download increment. SQL conflicts 409 without automatic retry; read then explicitly replay same request UUID/revision. Preview encrypted, 24h, five active per payer, purge 30d after expiry. Origin key ID survives deletion; revoke/transfer blocks confirmation. ",
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PortabilityImportReceipt"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing role/2FA/scope or real batch entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Outside boundary or origin missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/payload/source/SQL conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Expired/revoked preparation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Archive size/limits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid ZIP/manifest/selection/content",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preparation or download budget",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Normalizer unavailable or processing bound",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "file",
                  "request_id"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "request_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/portability/imports/{import}": {
      "get": {
        "operationId": "readPortableRestore",
        "tags": [
          "Portability"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "portability:import. Explicit scopes, owner/admin, verified email and 2FA. Current actor/payer/company ownership/location/key rechecked under locking reads. 25 QR/10 files/10 MiB uncompressed archive, manifest 2 MiB. Native export also after subscription ends, without reactivating resources; API keeps normal active entitlement. ZIP path/duplicate/bomb protections, no disk extraction and no network. Native selection is explicit; API codes/campaign exclusive or all accessible <=25. Export requires codes:read, pages:read if pages, history:read when history=true. Restoration requires real batch entitlement + codes:create, pages:write if pages, each new QR uses a place. New dynamic QR paused, pages draft, no old URLs/domains/team/payments/policies/active history restored. Protected Wi-Fi/raw entries excluded and shown in preview. Private no-store, no QR download increment. SQL conflicts 409 without automatic retry; read then explicitly replay same request UUID/revision. Preview encrypted, 24h, five active per payer, purge 30d after expiry. Origin key ID survives deletion; revoke/transfer blocks confirmation. ",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PortabilityImportReceipt"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing role/2FA/scope or real batch entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Outside boundary or origin missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/payload/source/SQL conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Expired/revoked preparation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Archive size/limits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid ZIP/manifest/selection/content",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preparation or download budget",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Normalizer unavailable or processing bound",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "import",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/portability/imports/{import}/confirm": {
      "post": {
        "operationId": "confirmPortableRestore",
        "tags": [
          "Portability"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "description": "portability:import + codes:create. Explicit scopes, owner/admin, verified email and 2FA. Current actor/payer/company ownership/location/key rechecked under locking reads. 25 QR/10 files/10 MiB uncompressed archive, manifest 2 MiB. Native export also after subscription ends, without reactivating resources; API keeps normal active entitlement. ZIP path/duplicate/bomb protections, no disk extraction and no network. Native selection is explicit; API codes/campaign exclusive or all accessible <=25. Export requires codes:read, pages:read if pages, history:read when history=true. Restoration requires real batch entitlement + codes:create, pages:write if pages, each new QR uses a place. New dynamic QR paused, pages draft, no old URLs/domains/team/payments/policies/active history restored. Protected Wi-Fi/raw entries excluded and shown in preview. Private no-store, no QR download increment. SQL conflicts 409 without automatic retry; read then explicitly replay same request UUID/revision. Preview encrypted, 24h, five active per payer, purge 30d after expiry. Origin key ID survives deletion; revoke/transfer blocks confirmation. ",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PortabilityImportReceipt"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing role/2FA/scope or real batch entitlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Outside boundary or origin missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision/payload/source/SQL conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Expired/revoked preparation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Archive size/limits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid ZIP/manifest/selection/content",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Preparation or download budget",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Normalizer unavailable or processing bound",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PortabilityConfirm"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "import",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/style-kits/{id}/brand": {
      "get": {
        "operationId": "getStyleKitBrand",
        "summary": "Read the private label identity",
        "tags": [
          "Style kits"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current identity with normalized raster, kits:read required. Private no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "revision": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "brand": {
                          "$ref": "#/components/schemas/BrandIdentity"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or expired key"
          },
          "403": {
            "description": "Permission or current role refused"
          },
          "404": {
            "description": "Not in workspace or archived"
          },
          "409": {
            "description": "Revision or SQL conflict; no partial retry"
          },
          "413": {
            "description": "Body or raster limit"
          },
          "422": {
            "description": "Validation failed"
          },
          "429": {
            "description": "Rate limit"
          },
          "503": {
            "description": "Image processing unavailable"
          }
        }
      },
      "put": {
        "operationId": "updateStyleKitBrand",
        "summary": "Replace label identity under current revision",
        "tags": [
          "Style kits"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "kits:write required, owner/admin for company, current 2FA and ownership. No current QR or email identity is modified. Write-only keys receive id/revision only. 400000-byte body maximum.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrandIdentityUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "id and new revision only"
          },
          "401": {
            "description": "Invalid or expired key"
          },
          "403": {
            "description": "Permission or current role refused"
          },
          "404": {
            "description": "Not in workspace or archived"
          },
          "409": {
            "description": "Revision or SQL conflict; no partial retry"
          },
          "413": {
            "description": "Body or raster limit"
          },
          "422": {
            "description": "Validation failed"
          },
          "429": {
            "description": "Rate limit"
          },
          "503": {
            "description": "Image processing unavailable"
          }
        }
      }
    },
    "/codes/{code}/nfc": {
      "get": {
        "operationId": "get__codes_code_nfc",
        "tags": [
          "NFC"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "nfc:read. Dynamic active links only; published page and non-retired asset required. Current actor, company, location, entitlement, key, revisions and HTTPS domain checked before delivery. No network request to the URL. No tag UID, hardware receipt or QR download/opening counted. Browser hardware access is local, explicit and bounded. A downloadable NDEF message is not a tag-specific memory dump.",
        "responses": {
          "200": {
            "description": "Current NDEF URL, never a hardware certificate",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NfcSnapshot"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over 32768 bytes; accommodates escaped valid Unicode notes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "summary": "Read the NFC URL record",
        "x-required-scope": "nfc:read"
      }
    },
    "/codes/{code}/nfc/prepare": {
      "post": {
        "operationId": "post__codes_code_nfc_prepare",
        "tags": [
          "NFC"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "description": "nfc:prepare. Dynamic active links only; published page and non-retired asset required. Current actor, company, location, entitlement, key, revisions and HTTPS domain checked before delivery. No network request to the URL. No tag UID, hardware receipt or QR download/opening counted. Browser hardware access is local, explicit and bounded. A downloadable NDEF message is not a tag-specific memory dump. Native editor or manager with current 2FA; API manager and explicit scope. Preparation expires after 60 seconds. The URL is not remotely written.",
        "responses": {
          "200": {
            "description": "Current NDEF URL, never a hardware certificate",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NfcSnapshot"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over 32768 bytes; accommodates escaped valid Unicode notes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "summary": "Authorize a local NFC preparation",
        "x-required-scope": "nfc:prepare",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "revision"
                ],
                "properties": {
                  "revision": {
                    "type": "string",
                    "pattern": "^[a-f0-9]{64}$"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/codes/{code}/nfc/export.{format}": {
      "get": {
        "operationId": "get__codes_code_nfc_export_format",
        "tags": [
          "NFC"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "format",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "ndef",
                "json"
              ]
            }
          }
        ],
        "description": "nfc:export. Dynamic active links only; published page and non-retired asset required. Current actor, company, location, entitlement, key, revisions and HTTPS domain checked before delivery. No network request to the URL. No tag UID, hardware receipt or QR download/opening counted. Browser hardware access is local, explicit and bounded. A downloadable NDEF message is not a tag-specific memory dump.",
        "responses": {
          "200": {
            "description": "Private whole file; raw NFC Forum URI record or versioned JSON; budget 5/minute/actor shared with inventory exports",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "integer",
                      "const": 1
                    },
                    "code_id": {
                      "type": "integer"
                    },
                    "records": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/NfcRecord"
                      }
                    },
                    "ndef_bytes": {
                      "type": "integer"
                    },
                    "sha256": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over 32768 bytes; accommodates escaped valid Unicode notes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "summary": "Export raw NDEF or Web NFC JSON",
        "x-required-scope": "nfc:export"
      }
    },
    "/symbols/render": {
      "post": {
        "operationId": "post_symbols_render",
        "tags": [
          "Product symbols"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [],
        "description": "symbols:render, current Business/batch entitlement and editor permission. Printable ASCII Data Matrix up to 256 characters; GS1 product subset AI01/10/21/17 with GTIN check digit and GS1 X82 characters. Complete expiry dates 2000..2049 only. Digital Link qualifiers in path, date in query, resolver origin supplied by operator and never contacted. Does not assign GS1 identifiers, create a resolver or a QRissimo redirect, or certify a physical mark. No QR place/opening/download created. Four-module white quiet zone, black square modules, no logo/style overrides. PNG uses integer pixels and refuses less than 3px/module; requested size is a maximum. Read current actor/payer/company/key/locations/ownership again after rendering. Budget5/minute/actor shared with inventory/NFC exports.",
        "responses": {
          "200": {
            "description": "Private full symbol or JSON preview; no business event created",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string"
                        },
                        "svg": {
                          "type": "string"
                        },
                        "matrix": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "integer",
                              "enum": [
                                0,
                                1
                              ]
                            }
                          }
                        },
                        "payload": {
                          "type": "string"
                        },
                        "readable": {
                          "type": "string"
                        },
                        "quiet_zone": {
                          "type": "integer",
                          "const": 4
                        },
                        "url": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, revoked or expired key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope, role, 2FA or actual entitlement refused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Object outside workspace/location, or missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Changed revision, scope or rights; no partial automatic retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Retired object refuses new interventions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body over 32768 bytes; accommodates escaped valid Unicode notes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid fields, dates, unique reference or technical history cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit; respect Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "summary": "Generate a Data Matrix or GS1 Digital Link QR",
        "x-required-scope": "symbols:render",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IndustrialSymbolRequest"
              },
              "examples": {
                "plain": {
                  "value": {
                    "type": "datamatrix",
                    "text": "PUMP-001",
                    "format": "svg"
                  }
                },
                "gs1": {
                  "value": {
                    "type": "gs1_datamatrix",
                    "gtin": "09520123456788",
                    "lot": "ABC1",
                    "serial": "12345",
                    "expiry": "2027-01-31",
                    "format": "png",
                    "size": 512
                  }
                },
                "link": {
                  "value": {
                    "type": "digital_link",
                    "gtin": "09520123456788",
                    "base_url": "https://id.example.org",
                    "format": "json"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/qr-logos/prepare": {
      "post": {
        "summary": "Normalize a small composition logo",
        "description": "Authenticated multipart PNG/JPEG upload, max 2 MiB. Edit role and images:render scope, server entitlement and budget. Returns a cleaned PNG data URL <=32768 characters and <=384 pixels per side; no code, image export or download is created.",
        "security": [
          {
            "bearer": []
          }
        ],
        "operationId": "prepareQrLogo",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "logo_file"
                ],
                "properties": {
                  "logo_file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Normalized private logo",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "string",
                      "maxLength": 32768,
                      "pattern": "^data:image/png;base64,"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing permission, entitlement or current workspace access."
          },
          "404": {
            "description": "Not found, trashed or outside the workspace."
          },
          "409": {
            "description": "QR changed during generation. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "422": {
            "description": "Invalid fields, size, density or contrast."
          },
          "429": {
            "description": "Rate limit; respect Retry-After."
          },
          "503": {
            "description": "GD image generation unavailable on this server."
          }
        }
      }
    },
    "/feedback/{entry}/files/{file}": {
      "get": {
        "summary": "Download a private feedback attachment",
        "operationId": "downloadFeedbackAttachment",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "entry",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "file",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Private PNG/JPEG/PDF. Current feedback:read scope and workspace/location checked before and after read. Separate download counter."
          },
          "403": {
            "description": "Missing scope/role"
          },
          "404": {
            "description": "Expired or invisible entry/file"
          },
          "409": {
            "description": "Content or permissions changed"
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "operationId": "get_webhooks",
        "summary": "List profiles and last30 deliveries",
        "description": "Requires webhooks:read. Secret encrypted, HTTPS public destination with proof before activation. No automatic retries of uncertain delivery. Runtime operator flag disabled by default. See docs/api/webhooks.md.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Accepted; 202 means queued, not received",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "profiles": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookProfile"
                          }
                        },
                        "messages": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WebhookMessage"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope, manage role, 2FA or current API entitlement"
          },
          "404": {
            "description": "Foreign or missing resource"
          },
          "409": {
            "description": "Changed revision, ownership or idempotency payload; no automatic write retry"
          },
          "422": {
            "description": "Invalid fields or confirmation"
          },
          "429": {
            "description": "Rate/envelope limit"
          }
        }
      },
      "post": {
        "operationId": "post_webhooks",
        "summary": "Create a recipient; secret one time",
        "description": "Requires webhooks:write. Secret encrypted, HTTPS public destination with proof before activation. No automatic retries of uncertain delivery. Runtime operator flag disabled by default. See docs/api/webhooks.md.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Identical replay, no secret"
          },
          "201": {
            "description": "Accepted; 202 means queued, not received",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookProfile"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope, manage role, 2FA or current API entitlement"
          },
          "404": {
            "description": "Foreign or missing resource"
          },
          "409": {
            "description": "Changed revision, ownership or idempotency payload; no automatic write retry"
          },
          "422": {
            "description": "Invalid fields or confirmation"
          },
          "429": {
            "description": "Rate/envelope limit"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              }
            }
          }
        }
      }
    },
    "/webhooks/{hook}/verify": {
      "post": {
        "operationId": "post_webhooks_verify",
        "summary": "Queue signed activation probe",
        "description": "Requires webhooks:write. Secret encrypted, HTTPS public destination with proof before activation. No automatic retries of uncertain delivery. Runtime operator flag disabled by default. See docs/api/webhooks.md.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted; 202 means queued, not received",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookMessage"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope, manage role, 2FA or current API entitlement"
          },
          "404": {
            "description": "Foreign or missing resource"
          },
          "409": {
            "description": "Changed revision, ownership or idempotency payload; no automatic write retry"
          },
          "422": {
            "description": "Invalid fields or confirmation"
          },
          "429": {
            "description": "Rate/envelope limit"
          }
        },
        "parameters": [
          {
            "in": "path",
            "name": "hook",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookRevision"
              }
            }
          }
        }
      }
    },
    "/webhooks/{hook}/disable": {
      "post": {
        "operationId": "post_webhooks_disable",
        "summary": "Disable and optionally rotate one-time secret",
        "description": "Requires webhooks:write. Secret encrypted, HTTPS public destination with proof before activation. No automatic retries of uncertain delivery. Runtime operator flag disabled by default. See docs/api/webhooks.md.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Accepted; 202 means queued, not received",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookProfile"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope, manage role, 2FA or current API entitlement"
          },
          "404": {
            "description": "Foreign or missing resource"
          },
          "409": {
            "description": "Changed revision, ownership or idempotency payload; no automatic write retry"
          },
          "422": {
            "description": "Invalid fields or confirmation"
          },
          "429": {
            "description": "Rate/envelope limit"
          }
        },
        "parameters": [
          {
            "in": "path",
            "name": "hook",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookDisable"
              }
            }
          }
        }
      }
    },
    "/webhooks/messages/{message}/retry": {
      "post": {
        "operationId": "post_webhooks_messages_retry",
        "summary": "Explicitly retry failed or uncertain delivery with unchanged event_id",
        "description": "Requires webhooks:write. Secret encrypted, HTTPS public destination with proof before activation. No automatic retries of uncertain delivery. Runtime operator flag disabled by default. See docs/api/webhooks.md.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted; 202 means queued, not received",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookMessage"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope, manage role, 2FA or current API entitlement"
          },
          "404": {
            "description": "Foreign or missing resource"
          },
          "409": {
            "description": "Changed revision, ownership or idempotency payload; no automatic write retry"
          },
          "410": {
            "description": "Event expired after two days"
          },
          "422": {
            "description": "Invalid fields or confirmation"
          },
          "429": {
            "description": "Rate/envelope limit"
          }
        },
        "parameters": [
          {
            "in": "path",
            "name": "message",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookConfirm"
              }
            }
          }
        }
      }
    },
    "/nfc/batches/preview": {
      "post": {
        "operationId": "post_nfc_batches_preview",
        "summary": "Preview1..100 owned active QR NFC records",
        "description": "Requires nfc:read. See API guide.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Complete result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/NfcBatchManifest"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope or current role/entitlement"
          },
          "404": {
            "description": "Foreign or missing resource"
          },
          "409": {
            "description": "Revision or rights changed; no partial result or automatic retry"
          },
          "422": {
            "description": "Invalid input"
          },
          "429": {
            "description": "Budget exhausted"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NfcBatchPreview"
              }
            }
          }
        }
      }
    },
    "/nfc/batches/export": {
      "post": {
        "operationId": "post_nfc_batches_export",
        "summary": "Export current checked NFC batch; no physical writing",
        "description": "Requires nfc:export. See API guide. JSON or CSV or ZIP(manifest,links,records/qr-ID.ndef),512Kio manifest/1Mio ZIP. Recheck all rights/publication/revisions after rendering, shared5/min actor budget, no scans/downloadsQR incremented.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Complete result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NfcBatchManifest"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              },
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope or current role/entitlement"
          },
          "404": {
            "description": "Foreign or missing resource"
          },
          "409": {
            "description": "Revision or rights changed; no partial result or automatic retry"
          },
          "422": {
            "description": "Invalid input"
          },
          "429": {
            "description": "Budget exhausted"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NfcBatchExport"
              }
            }
          }
        }
      }
    },
    "/brand-rules": {
      "get": {
        "operationId": "get_brand_rules",
        "summary": "Read current central brand rules",
        "description": "Requires brand_rules:read. See API guide.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Complete result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BrandRule"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope or current role/entitlement"
          },
          "404": {
            "description": "Foreign or missing resource"
          },
          "409": {
            "description": "Revision or rights changed; no partial result or automatic retry"
          },
          "422": {
            "description": "Invalid input"
          },
          "429": {
            "description": "Budget exhausted"
          }
        }
      },
      "put": {
        "operationId": "put_brand_rules",
        "summary": "Explicitly set mandatory design values from a versioned style kit",
        "description": "Requires brand_rules:write. See API guide.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Complete result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "revision": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "enabled": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope or current role/entitlement"
          },
          "404": {
            "description": "Foreign or missing resource"
          },
          "409": {
            "description": "Revision or rights changed; no partial result or automatic retry"
          },
          "422": {
            "description": "Invalid input"
          },
          "429": {
            "description": "Budget exhausted"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrandRuleUpdate"
              }
            }
          }
        }
      }
    },
    "/print-labels": {
      "post": {
        "summary": "Export full PNG/JPEG labels with literal variables as one private ZIP",
        "description": "Requires prints:export and kits:read if brand_id supplied.1..16distinct owned QR codes.2048px longest side,12MiB images/16MiB ZIP. One image per QR, no copies/skip/crop marks; identical label geometry/text. Recheck complete rights/key/content/brand after render before counters/remise. No new QR/scan/email. See docs/api/printing.md.",
        "security": [
          {
            "bearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "codes",
                  "layout",
                  "format"
                ],
                "properties": {
                  "codes": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 16,
                    "uniqueItems": true,
                    "items": {
                      "type": "integer",
                      "minimum": 1
                    }
                  },
                  "layout": {
                    "oneOf": [
                      {
                        "type": "integer",
                        "enum": [
                          2,
                          6,
                          12,
                          24
                        ]
                      },
                      {
                        "type": "string",
                        "const": "custom"
                      }
                    ]
                  },
                  "labels": {
                    "type": "boolean",
                    "default": false
                  },
                  "print": {
                    "$ref": "#/components/schemas/PrintGeometry"
                  },
                  "brand_id": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1,
                    "description": "Optional active style kit in the same workspace; requires kits:read in addition to prints:export. Applies the label identity, never replaces the saved QR drawing."
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "png",
                      "jpeg"
                    ]
                  }
                },
                "examples": [
                  {
                    "codes": [
                      42,
                      43
                    ],
                    "layout": 12,
                    "labels": true
                  },
                  {
                    "codes": [
                      42,
                      43
                    ],
                    "layout": "custom",
                    "labels": true,
                    "brand_id": 5,
                    "print": {
                      "orientation": "portrait",
                      "width": 60,
                      "height": 60,
                      "gap_x": 5,
                      "gap_y": 5,
                      "copies": 4,
                      "skip": 2,
                      "crop_marks": true
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Complete private archive with manifest and one image per QR",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked key."
          },
          "403": {
            "description": "Missing scope, subscription or current workspace access."
          },
          "404": {
            "description": "At least one code is absent, trashed or outside the key workspace."
          },
          "409": {
            "description": "Conflict. Access or permissions may have changed while the result was prepared. Do not automatically replay writes; use the documented idempotency identifier and check their state."
          },
          "413": {
            "description": "Document/input size limit; no file or download count."
          },
          "422": {
            "description": "Invalid selection/layout, unsupported field, or too dense to print."
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "503": {
            "description": "Bounded PDF worker unavailable; no file or download count."
          }
        },
        "operationId": "post_print_labels"
      }
    },
    "/page-templates/{type}": {
      "get": {
        "operationId": "get_page_template",
        "summary": "Read a central editorial template",
        "description": "Requires page_templates:read, current content role and Business entitlement. Returns shared immutable editorial snapshot, not live private source files.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "links",
                "menu",
                "guide",
                "product",
                "event",
                "app",
                "article",
                "pass"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Complete result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PageTemplate"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope or current role/entitlement"
          },
          "404": {
            "description": "Foreign, missing or inaccessible resource"
          },
          "409": {
            "description": "Revision, rights or ownership changed; no partial result or automatic retry"
          },
          "413": {
            "description": "Body too large (8 KiB write maximum)"
          },
          "422": {
            "description": "Invalid input or incompatible mandatory contents"
          },
          "429": {
            "description": "Budget exhausted"
          }
        }
      },
      "put": {
        "operationId": "update_page_template",
        "summary": "Configure central mandatory content",
        "description": "Requires page_templates:write and pages:read when enabled. Global owner/admin with 2FA and Business; no location-bound API key. Snapshot cannot be supplied by the client.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "links",
                "menu",
                "guide",
                "product",
                "event",
                "app",
                "article",
                "pass"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Complete result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "revision": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "enabled": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "revision",
                        "enabled"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope or current role/entitlement"
          },
          "404": {
            "description": "Foreign, missing or inaccessible resource"
          },
          "409": {
            "description": "Revision, rights or ownership changed; no partial result or automatic retry"
          },
          "413": {
            "description": "Body too large (8 KiB write maximum)"
          },
          "422": {
            "description": "Invalid input or incompatible mandatory contents"
          },
          "429": {
            "description": "Budget exhausted"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PageTemplateUpdate"
              }
            }
          }
        }
      }
    },
    "/pages/{page}/template-preview": {
      "get": {
        "operationId": "preview_page_template",
        "summary": "Prepare central adoption without writing",
        "description": "Requires page_templates:read and pages:read. Rechecks current workspace, location, ownership and both revisions. No publication, opening or download counted.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "revision",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "template_revision",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Complete result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "page_id",
                        "revision",
                        "template_revision",
                        "draft"
                      ],
                      "properties": {
                        "page_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "revision": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "template_revision": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "draft": {
                          "$ref": "#/components/schemas/HostedPageSnapshot"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope or current role/entitlement"
          },
          "404": {
            "description": "Foreign, missing or inaccessible resource"
          },
          "409": {
            "description": "Revision, rights or ownership changed; no partial result or automatic retry"
          },
          "413": {
            "description": "Body too large (8 KiB write maximum)"
          },
          "422": {
            "description": "Invalid input or incompatible mandatory contents"
          },
          "429": {
            "description": "Budget exhausted"
          }
        }
      }
    },
    "/pages/{page}/template": {
      "post": {
        "operationId": "apply_page_template",
        "summary": "Apply the current template to a page draft",
        "description": "Requires page_templates:apply and pages:write. Explicit confirmation, whole draft updated atomically, free content preserved, no automatic publication. Rejects incompatibility or >40 sections per language.",
        "security": [
          {
            "bearer": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Complete result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "id",
                        "revision"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "revision": {
                          "type": "string",
                          "format": "uuid"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Missing scope or current role/entitlement"
          },
          "404": {
            "description": "Foreign, missing or inaccessible resource"
          },
          "409": {
            "description": "Revision, rights or ownership changed; no partial result or automatic retry"
          },
          "413": {
            "description": "Body too large (8 KiB write maximum)"
          },
          "422": {
            "description": "Invalid input or incompatible mandatory contents"
          },
          "429": {
            "description": "Budget exhausted"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PageTemplateApply"
              }
            }
          }
        }
      }
    },
    "/mail": {
      "get": {
        "operationId": "listMailDelivery",
        "summary": "listMailDelivery",
        "description": "Requires mail:read, current owner/admin permissions and 2FA. No legacy scope inheritance. Native changes additionally require a recent password/2FA confirmation. Settings apply to the whole current workspace; location-bound keys are refused. Verified DNS expires after 24 hours; only an operator-approved authenticated relay and allowlisted domain can be activated. Security/account mail keeps the fixed QRissimo sender. Bounce and complaint events are reported by a separately authenticated collector, not by this API key. No automatic resend of uncertain or deferred messages. Releasing a complaint requires the service owner. Revisions prevent lost updates; conflicts are never partially retried. Read returns one sender and messages (30) and active suppressions (20), independently paginated by messages_page and pauses_page. Recipients are masked.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MailJournal"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Insufficient role, 2FA, entitlement or scope"
          },
          "409": {
            "description": "Changed revision, in-flight mail, SQL conflict, or operator DNS setup pending"
          },
          "422": {
            "description": "Invalid data or unverified domain/relay"
          },
          "429": {
            "description": "Write or DNS verification budget exceeded"
          }
        },
        "parameters": [
          {
            "in": "query",
            "name": "messages_page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "in": "query",
            "name": "pauses_page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000
            }
          }
        ]
      }
    },
    "/mail/sender": {
      "put": {
        "operationId": "prepareMailSender",
        "summary": "prepareMailSender",
        "description": "Requires mail:write, current owner/admin permissions and 2FA. No legacy scope inheritance. Native changes additionally require a recent password/2FA confirmation. Settings apply to the whole current workspace; location-bound keys are refused. Verified DNS expires after 24 hours; only an operator-approved authenticated relay and allowlisted domain can be activated. Security/account mail keeps the fixed QRissimo sender. Bounce and complaint events are reported by a separately authenticated collector, not by this API key. No automatic resend of uncertain or deferred messages. Releasing a complaint requires the service owner. Revisions prevent lost updates; conflicts are never partially retried. Read returns one sender and messages (30) and active suppressions (20), independently paginated by messages_page and pauses_page. Recipients are masked.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MailSender"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Insufficient role, 2FA, entitlement or scope"
          },
          "409": {
            "description": "Changed revision, in-flight mail, SQL conflict, or operator DNS setup pending"
          },
          "422": {
            "description": "Invalid data or unverified domain/relay"
          },
          "429": {
            "description": "Write or DNS verification budget exceeded"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailSenderInput"
              }
            }
          }
        }
      }
    },
    "/mail/senders/{sender}/check": {
      "post": {
        "operationId": "checkMailSender",
        "summary": "checkMailSender",
        "description": "Requires mail:check, current owner/admin permissions and 2FA. No legacy scope inheritance. Native changes additionally require a recent password/2FA confirmation. Settings apply to the whole current workspace; location-bound keys are refused. Verified DNS expires after 24 hours; only an operator-approved authenticated relay and allowlisted domain can be activated. Security/account mail keeps the fixed QRissimo sender. Bounce and complaint events are reported by a separately authenticated collector, not by this API key. No automatic resend of uncertain or deferred messages. Releasing a complaint requires the service owner. Revisions prevent lost updates; conflicts are never partially retried. Read returns one sender and messages (30) and active suppressions (20), independently paginated by messages_page and pauses_page. Recipients are masked.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "const": "queued"
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Insufficient role, 2FA, entitlement or scope"
          },
          "409": {
            "description": "Changed revision, in-flight mail, SQL conflict, or operator DNS setup pending"
          },
          "422": {
            "description": "Invalid data or unverified domain/relay"
          },
          "429": {
            "description": "Write or DNS verification budget exceeded"
          }
        },
        "parameters": [
          {
            "in": "path",
            "name": "sender",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailRevisionInput"
              }
            }
          }
        }
      }
    },
    "/mail/senders/{sender}/state": {
      "post": {
        "operationId": "configureMailSender",
        "summary": "configureMailSender",
        "description": "Requires mail:write, current owner/admin permissions and 2FA. No legacy scope inheritance. Native changes additionally require a recent password/2FA confirmation. Settings apply to the whole current workspace; location-bound keys are refused. Verified DNS expires after 24 hours; only an operator-approved authenticated relay and allowlisted domain can be activated. Security/account mail keeps the fixed QRissimo sender. Bounce and complaint events are reported by a separately authenticated collector, not by this API key. No automatic resend of uncertain or deferred messages. Releasing a complaint requires the service owner. Revisions prevent lost updates; conflicts are never partially retried. Read returns one sender and messages (30) and active suppressions (20), independently paginated by messages_page and pauses_page. Recipients are masked.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MailSender"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Insufficient role, 2FA, entitlement or scope"
          },
          "409": {
            "description": "Changed revision, in-flight mail, SQL conflict, or operator DNS setup pending"
          },
          "422": {
            "description": "Invalid data or unverified domain/relay"
          },
          "429": {
            "description": "Write or DNS verification budget exceeded"
          }
        },
        "parameters": [
          {
            "in": "path",
            "name": "sender",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailSenderStateInput"
              }
            }
          }
        }
      }
    },
    "/mail/suppressions/{pause}/release": {
      "post": {
        "operationId": "releaseMailSuppression",
        "summary": "releaseMailSuppression",
        "description": "Requires mail:suppressions, current owner/admin permissions and 2FA. No legacy scope inheritance. Native changes additionally require a recent password/2FA confirmation. Settings apply to the whole current workspace; location-bound keys are refused. Verified DNS expires after 24 hours; only an operator-approved authenticated relay and allowlisted domain can be activated. Security/account mail keeps the fixed QRissimo sender. Bounce and complaint events are reported by a separately authenticated collector, not by this API key. No automatic resend of uncertain or deferred messages. Releasing a complaint requires the service owner. Revisions prevent lost updates; conflicts are never partially retried. Read returns one sender and messages (30) and active suppressions (20), independently paginated by messages_page and pauses_page. Recipients are masked.",
        "security": [
          {
            "bearer": []
          }
        ],
        "responses": {
          "204": {
            "description": "Success"
          },
          "401": {
            "description": "Invalid key"
          },
          "403": {
            "description": "Insufficient role, 2FA, entitlement or scope"
          },
          "409": {
            "description": "Changed revision, in-flight mail, SQL conflict, or operator DNS setup pending"
          },
          "422": {
            "description": "Invalid data or unverified domain/relay"
          },
          "429": {
            "description": "Write or DNS verification budget exceeded"
          }
        },
        "parameters": [
          {
            "in": "path",
            "name": "pause",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailRevisionInput"
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Hashed expiring/revocable API key. Explicit scopes: codes:read, codes:create, codes:update, codes:style, codes:state, codes:export, images:render, prints:export, batches:read, batches:write, batches:export, statistics:read, campaigns:read, campaigns:write, catalog:read. A key never increases account entitlements. History uses explicit history:read/history:restore scopes; reading an old content also requires codes:read. Inventory uses explicit assets:read, assets:configure, assets:checks and assets:history; no legacy inheritance. Reference CSV uses assets:export explicitly. Price/stock uses commerce:read, commerce:configure, commerce:write, commerce:export explicitly. Destination monitoring requires explicit monitoring:read/write/check/alerts scopes. Reports require explicit reports:read/create/email/schedule; creation also statistics:read. No legacy inheritance."
      }
    },
    "schemas": {
      "QrCode": {
        "type": "object",
        "required": [
          "id",
          "label",
          "kind",
          "destination",
          "short_url",
          "status",
          "svg_url",
          "content_type",
          "content"
        ],
        "properties": {
          "label": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "destination": {
            "type": "string"
          },
          "short_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Stable short URL for dynamic codes; null for static codes."
          },
          "status": {
            "type": "string"
          },
          "svg_url": {
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "starts_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "UTC instant."
          },
          "ends_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "UTC instant."
          },
          "timezone": {
            "type": "string",
            "enum": [
              "Europe/Paris",
              "Europe/London",
              "Europe/Berlin",
              "Europe/Rome",
              "Europe/Madrid",
              "Europe/Lisbon",
              "Europe/Bucharest",
              "Europe/Moscow",
              "Europe/Kyiv",
              "Europe/Amsterdam",
              "Europe/Warsaw",
              "Europe/Prague",
              "Europe/Bratislava",
              "Europe/Budapest",
              "Europe/Athens",
              "Europe/Sofia",
              "Europe/Zagreb",
              "Europe/Belgrade",
              "Europe/Istanbul",
              "Europe/Stockholm",
              "Europe/Copenhagen",
              "Europe/Helsinki",
              "Europe/Oslo",
              "UTC",
              "America/New_York",
              "America/Los_Angeles",
              "America/Sao_Paulo"
            ]
          },
          "fallback_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "maxLength": 1000,
            "description": "HTTP(S) only. Requires ends_at. Used after expiry only, never for disabled codes, unpaid subscriptions or suspended accounts."
          },
          "content_type": {
            "type": "string",
            "enum": [
              "url",
              "text",
              "wifi",
              "vcard",
              "email",
              "phone",
              "sms",
              "whatsapp",
              "raw",
              "page"
            ],
            "default": "url"
          },
          "content": {
            "type": "object",
            "description": "Typed content fields. Wi-Fi passwords and raw imported content are omitted. Raw content is an empty object."
          },
          "style": {
            "$ref": "#/components/schemas/QrAppearance"
          },
          "page_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "domain_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Verified active domain in this workspace. Immutable on QR updates; null uses QRissimo."
          }
        }
      },
      "CreateQrCode": {
        "type": "object",
        "required": [
          "label"
        ],
        "properties": {
          "label": {
            "type": "string",
            "maxLength": 255
          },
          "destination": {
            "type": "string",
            "format": "uri",
            "maxLength": 1000,
            "description": "Only http and https."
          },
          "kind": {
            "type": "string",
            "enum": [
              "static",
              "dynamic"
            ],
            "description": "Defaults to dynamic except for raw imports, which default to static and reject dynamic."
          },
          "modules": {
            "enum": [
              "square",
              "rounded",
              "dot"
            ],
            "default": "square"
          },
          "finders": {
            "enum": [
              "square",
              "rounded",
              "circle"
            ],
            "default": "square"
          },
          "template": {
            "description": "Catalogue identifier. The retired guitar identifier remains accepted for compatibility and is normalized to vinyl; new integrations should use vinyl.",
            "enum": [
              "plain",
              "card",
              "round",
              "seal",
              "corners",
              "bracket",
              "bubble",
              "tag",
              "ticket",
              "band",
              "polaroid",
              "heart",
              "arch",
              "fold",
              "capsule",
              "hexagon",
              "orbit",
              "ripple",
              "editorial",
              "rails",
              "ribbon",
              "pennant",
              "grid",
              "rosette",
              "car",
              "van",
              "bus",
              "bicycle",
              "rocket",
              "plane",
              "boat",
              "train",
              "cat",
              "dog",
              "bear",
              "fox",
              "rabbit",
              "owl",
              "butterfly",
              "flower",
              "leaf",
              "tree",
              "house",
              "camera",
              "phone",
              "laptop",
              "cup",
              "book",
              "bulb",
              "gift",
              "bottle",
              "lock",
              "gamepad",
              "football",
              "basketball",
              "music",
              "vinyl",
              "cinema",
              "palette",
              "crown"
            ],
            "default": "plain"
          },
          "arrow": {
            "enum": [
              "none",
              "curve",
              "wave",
              "loop"
            ],
            "default": "none"
          },
          "foreground": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#182B2A"
          },
          "background": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#FFFFFF"
          },
          "caption": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30,
            "default": ""
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "content_type": {
            "type": "string",
            "enum": [
              "url",
              "text",
              "wifi",
              "vcard",
              "email",
              "phone",
              "sms",
              "whatsapp",
              "raw",
              "page"
            ],
            "default": "url"
          },
          "text": {
            "type": "string",
            "maxLength": 1200
          },
          "wifi_ssid": {
            "type": "string",
            "maxLength": 32,
            "description": "Required for wifi. At most 32 UTF-8 bytes. Whitespace is preserved."
          },
          "wifi_password": {
            "type": "string",
            "maxLength": 64,
            "description": "Required for WPA: 8–63 bytes, or 64 hexadecimal characters. Blank on PATCH retains the current secret. Encrypted at rest and never returned in JSON. A static WIFI QR necessarily contains the password: share guest networks only."
          },
          "contact_first": {
            "type": "string",
            "maxLength": 100
          },
          "contact_last": {
            "type": "string",
            "maxLength": 100
          },
          "contact_org": {
            "type": "string",
            "maxLength": 150
          },
          "contact_title": {
            "type": "string",
            "maxLength": 150
          },
          "contact_phone": {
            "type": "string",
            "maxLength": 40,
            "description": "International + followed by 6–15 digits. Spaces, parentheses, dots and dashes are removed."
          },
          "contact_email": {
            "type": "string",
            "maxLength": 254,
            "format": "email"
          },
          "contact_url": {
            "type": "string",
            "maxLength": 500,
            "format": "uri"
          },
          "contact_street": {
            "type": "string",
            "maxLength": 150
          },
          "contact_city": {
            "type": "string",
            "maxLength": 150
          },
          "contact_zip": {
            "type": "string",
            "maxLength": 150
          },
          "contact_country": {
            "type": "string",
            "maxLength": 150
          },
          "email_to": {
            "type": "string",
            "maxLength": 254,
            "format": "email"
          },
          "email_subject": {
            "type": "string",
            "maxLength": 150
          },
          "phone": {
            "type": "string",
            "maxLength": 40,
            "description": "International + followed by 6–15 digits. Spaces, parentheses, dots and dashes are removed."
          },
          "message": {
            "type": "string",
            "maxLength": 1000,
            "description": "Optional email/SMS/WhatsApp body. Unicode and whitespace preserved."
          },
          "wifi_security": {
            "type": "string",
            "enum": [
              "WPA",
              "nopass"
            ]
          },
          "wifi_hidden": {
            "type": "boolean",
            "default": false
          },
          "raw": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1200,
            "writeOnly": true,
            "description": "Imported UTF-8 text, unchanged including whitespace. No binary data or control characters other than tab/CR/LF. Static QR only; kind defaults to static for raw. Stored encrypted and never returned in JSON. Normal QR capacity and readability limits apply."
          },
          "page_type": {
            "type": "string",
            "enum": [
              "links",
              "menu",
              "guide",
              "product",
              "event",
              "app",
              "article",
              "pass"
            ],
            "description": "Required for content_type page. Only dynamic QR permitted; creates an empty private draft in the QR transaction. API also requires pages:write, including nonce replays."
          },
          "domain_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Verified active workspace domain for a new dynamic QR; immutable afterwards."
          },
          "caption_font": {
            "type": "string",
            "enum": [
              "sans",
              "serif",
              "mono",
              "caveat",
              "patrick_hand"
            ],
            "description": "Caption font. Handwritten Caveat or Patrick Hand uses outlined glyphs in SVG/PDF and a bundled pinned font in PNG/JPEG. Supported fallback uses sans, unavailable glyphs use a missing-character box."
          },
          "logo_data": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 32768,
            "description": "Normalized PNG data URI, max384px per side and32768characters; empty removes. Center included in the QR-only image; header/callout require composition."
          },
          "caption_position": {
            "type": "string",
            "enum": [
              "auto",
              "top",
              "bottom",
              "left",
              "right"
            ],
            "default": "auto",
            "description": "Position du texte et de la flèche dans une bande extérieure réservée. auto conserve le modèle validé."
          },
          "caption_offset": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 50,
            "description": "Décalage horizontal (haut/bas) ou vertical (gauche/droite), en pourcentage de la plage sûre."
          },
          "caption_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Taille du texte extérieur, en pourcentage. Sans effet dans le placement auto."
          },
          "caption_align": {
            "type": "string",
            "enum": [
              "left",
              "center",
              "right"
            ],
            "default": "center"
          },
          "composition_mode": {
            "type": "string",
            "enum": [
              "legacy",
              "free"
            ],
            "default": "legacy",
            "description": "Any nondefault option or handwritten caption promotes free mode; auto placement becomes bottom. All editable elements remain in a protected exterior band."
          },
          "logo_mode": {
            "type": "string",
            "enum": [
              "header",
              "callout",
              "center"
            ],
            "default": "header",
            "description": "Center uses error correction H. The visible logo spans at least20% of the complete QR (quiet zone included), target25%, at most30%, preserving all functional patterns. A larger encoding version may be needed; unsuitable content or failed server decoding is refused. Center offsets must be zero."
          },
          "composition_layers": {
            "type": "string",
            "enum": [
              "logo-arrow-text",
              "logo-text-arrow",
              "arrow-logo-text",
              "arrow-text-logo",
              "text-logo-arrow",
              "text-arrow-logo"
            ],
            "default": "logo-arrow-text",
            "description": "Paint order from back to front. The QR and artwork are never reordered."
          },
          "caption_rotation": {
            "type": "integer",
            "minimum": -180,
            "maximum": 180,
            "default": 0,
            "description": "Degrees, clockwise; -180 normalized to 180."
          },
          "caption_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "caption_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_scale": {
            "type": "integer",
            "minimum": 50,
            "maximum": 150,
            "default": 100,
            "description": "50..150 preset scale. For center, the visible longest side is never below20% or above30% of the complete QR, subject to safe module steps. Exterior presets retain their existing behavior."
          },
          "caption_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "arrow_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "color_mode": {
            "type": "string",
            "enum": [
              "solid",
              "linear",
              "radial"
            ],
            "default": "solid"
          },
          "gradient_color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Required for linear/radial. Both endpoints must pass contrast controls. Function patterns stay solid."
          },
          "gradient_angle": {
            "type": "integer",
            "minimum": 0,
            "maximum": 359,
            "default": 45,
            "description": "Clockwise, linear only."
          }
        },
        "description": "Eight static or editable types and imported raw text (static only). Types are permanent. API entitlement and codes:create scope required, including eligible managed access. Wi-Fi passwords and raw text are write-only. URL default preserves existing integrations.",
        "allOf": [
          {
            "if": {
              "properties": {
                "content_type": {
                  "const": "url"
                }
              }
            },
            "then": {
              "required": [
                "destination"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "content_type": {
                  "const": "text"
                }
              },
              "required": [
                "content_type"
              ]
            },
            "then": {
              "required": [
                "text"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "content_type": {
                  "const": "wifi"
                }
              },
              "required": [
                "content_type"
              ]
            },
            "then": {
              "required": [
                "wifi_ssid",
                "wifi_security"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "content_type": {
                  "const": "email"
                }
              },
              "required": [
                "content_type"
              ]
            },
            "then": {
              "required": [
                "email_to"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "content_type": {
                  "const": "phone"
                }
              },
              "required": [
                "content_type"
              ]
            },
            "then": {
              "required": [
                "phone"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "content_type": {
                  "const": "sms"
                }
              },
              "required": [
                "content_type"
              ]
            },
            "then": {
              "required": [
                "phone"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "content_type": {
                  "const": "whatsapp"
                }
              },
              "required": [
                "content_type"
              ]
            },
            "then": {
              "required": [
                "phone"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "content_type": {
                  "const": "wifi"
                },
                "wifi_security": {
                  "const": "WPA"
                }
              },
              "required": [
                "content_type",
                "wifi_security"
              ]
            },
            "then": {
              "required": [
                "wifi_password"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "content_type": {
                  "const": "vcard"
                }
              },
              "required": [
                "content_type"
              ]
            },
            "then": {
              "anyOf": [
                {
                  "required": [
                    "contact_first"
                  ]
                },
                {
                  "required": [
                    "contact_last"
                  ]
                }
              ]
            }
          },
          {
            "if": {
              "properties": {
                "content_type": {
                  "const": "raw"
                }
              },
              "required": [
                "content_type"
              ]
            },
            "then": {
              "required": [
                "raw"
              ],
              "properties": {
                "kind": {
                  "const": "static"
                }
              }
            }
          }
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "errors": {
            "type": "object"
          }
        }
      },
      "Campaign": {
        "type": "object",
        "required": [
          "id",
          "name",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "qr_codes_count": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "QrPeriodStatistics": {
        "allOf": [
          {
            "$ref": "#/components/schemas/QrSummary"
          },
          {
            "type": "object",
            "properties": {
              "archived": {
                "type": "boolean"
              },
              "campaign": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "openings_measured": {
                "type": "boolean"
              },
              "period_openings": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "period_downloads": {
                "type": "integer"
              },
              "first_opening": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "last_opening": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "period_page_clicks": {
                "type": "integer"
              },
              "period_page_file_downloads": {
                "type": "integer"
              },
              "period_restaurant_received": {
                "type": "integer",
                "minimum": 0
              },
              "period_restaurant_confirmed": {
                "type": "integer",
                "minimum": 0
              }
            }
          }
        ]
      },
      "StatisticsReport": {
        "type": "object",
        "required": [
          "period",
          "summary",
          "daily"
        ],
        "properties": {
          "period": {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "format": "date"
              },
              "to": {
                "type": "string",
                "format": "date"
              },
              "timezone": {
                "type": "string",
                "enum": [
                  "Europe/Paris",
                  "Europe/London",
                  "Europe/Berlin",
                  "Europe/Rome",
                  "Europe/Madrid",
                  "Europe/Lisbon",
                  "Europe/Bucharest",
                  "Europe/Moscow",
                  "Europe/Kyiv",
                  "Europe/Amsterdam",
                  "Europe/Warsaw",
                  "Europe/Prague",
                  "Europe/Bratislava",
                  "Europe/Budapest",
                  "Europe/Athens",
                  "Europe/Sofia",
                  "Europe/Zagreb",
                  "Europe/Belgrade",
                  "Europe/Istanbul",
                  "Europe/Stockholm",
                  "Europe/Copenhagen",
                  "Europe/Helsinki",
                  "Europe/Oslo",
                  "UTC",
                  "America/New_York",
                  "America/Los_Angeles",
                  "America/Sao_Paulo"
                ]
              }
            }
          },
          "summary": {
            "type": "object",
            "properties": {
              "codes": {
                "type": "integer",
                "minimum": 0
              },
              "measured_codes": {
                "type": "integer",
                "minimum": 0
              },
              "used_codes": {
                "type": "integer",
                "minimum": 0
              },
              "openings": {
                "type": "integer",
                "minimum": 0
              },
              "downloads": {
                "type": "integer",
                "minimum": 0
              },
              "first_opening": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "last_opening": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "page_clicks": {
                "type": "integer",
                "description": "Observed public link clicks excluding identified bots, not confirmed conversions."
              },
              "page_bot_actions": {
                "type": "integer"
              },
              "page_file_downloads": {
                "type": "integer",
                "description": "Observed public PDF requests excluding identified bots, HEAD and prefetch. Not distinct visitors; separate from QR exports and link clicks."
              },
              "restaurant_received": {
                "type": "integer",
                "minimum": 0
              },
              "restaurant_confirmed": {
                "type": "integer",
                "minimum": 0
              },
              "feedback_received": {
                "type": "integer",
                "minimum": 0
              }
            }
          },
          "daily": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "date",
                "openings",
                "downloads"
              ],
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "openings": {
                  "type": "integer",
                  "minimum": 0
                },
                "downloads": {
                  "type": "integer",
                  "minimum": 0
                },
                "page_clicks": {
                  "type": "integer"
                },
                "restaurant_received": {
                  "type": "integer",
                  "minimum": 0
                },
                "restaurant_confirmed": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "codes": {
            "type": "object",
            "required": [
              "data",
              "current_page",
              "last_page",
              "total"
            ],
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/QrPeriodStatistics"
                }
              },
              "current_page": {
                "type": "integer"
              },
              "last_page": {
                "type": "integer"
              },
              "total": {
                "type": "integer"
              }
            }
          }
        }
      },
      "QrAppearance": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "modules": {
            "enum": [
              "square",
              "rounded",
              "dot"
            ],
            "default": "square"
          },
          "finders": {
            "enum": [
              "square",
              "rounded",
              "circle"
            ],
            "default": "square"
          },
          "template": {
            "description": "Catalogue identifier. The retired guitar identifier remains accepted for compatibility and is normalized to vinyl; new integrations should use vinyl.",
            "enum": [
              "plain",
              "card",
              "round",
              "seal",
              "corners",
              "bracket",
              "bubble",
              "tag",
              "ticket",
              "band",
              "polaroid",
              "heart",
              "arch",
              "fold",
              "capsule",
              "hexagon",
              "orbit",
              "ripple",
              "editorial",
              "rails",
              "ribbon",
              "pennant",
              "grid",
              "rosette",
              "car",
              "van",
              "bus",
              "bicycle",
              "rocket",
              "plane",
              "boat",
              "train",
              "cat",
              "dog",
              "bear",
              "fox",
              "rabbit",
              "owl",
              "butterfly",
              "flower",
              "leaf",
              "tree",
              "house",
              "camera",
              "phone",
              "laptop",
              "cup",
              "book",
              "bulb",
              "gift",
              "bottle",
              "lock",
              "gamepad",
              "football",
              "basketball",
              "music",
              "vinyl",
              "cinema",
              "palette",
              "crown"
            ],
            "default": "plain"
          },
          "arrow": {
            "enum": [
              "none",
              "curve",
              "wave",
              "loop"
            ],
            "default": "none"
          },
          "foreground": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#182B2A"
          },
          "background": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#FFFFFF"
          },
          "caption": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30,
            "default": ""
          },
          "caption_font": {
            "type": "string",
            "enum": [
              "sans",
              "serif",
              "mono",
              "caveat",
              "patrick_hand"
            ],
            "description": "Caption font. Handwritten Caveat or Patrick Hand uses outlined glyphs in SVG/PDF and a bundled pinned font in PNG/JPEG. Supported fallback uses sans, unavailable glyphs use a missing-character box."
          },
          "logo_data": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 32768,
            "description": "Normalized PNG data URI, max384px per side and32768characters; empty removes. Center included in the QR-only image; header/callout require composition."
          },
          "caption_position": {
            "type": "string",
            "enum": [
              "auto",
              "top",
              "bottom",
              "left",
              "right"
            ],
            "default": "auto",
            "description": "Position du texte et de la flèche dans une bande extérieure réservée. auto conserve le modèle validé."
          },
          "caption_offset": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 50,
            "description": "Décalage horizontal (haut/bas) ou vertical (gauche/droite), en pourcentage de la plage sûre."
          },
          "caption_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Taille du texte extérieur, en pourcentage. Sans effet dans le placement auto."
          },
          "caption_align": {
            "type": "string",
            "enum": [
              "left",
              "center",
              "right"
            ],
            "default": "center"
          },
          "composition_mode": {
            "type": "string",
            "enum": [
              "legacy",
              "free"
            ],
            "default": "legacy",
            "description": "Any nondefault option or handwritten caption promotes free mode; auto placement becomes bottom. All editable elements remain in a protected exterior band."
          },
          "logo_mode": {
            "type": "string",
            "enum": [
              "header",
              "callout",
              "center"
            ],
            "default": "header",
            "description": "Center uses error correction H. The visible logo spans at least20% of the complete QR (quiet zone included), target25%, at most30%, preserving all functional patterns. A larger encoding version may be needed; unsuitable content or failed server decoding is refused. Center offsets must be zero."
          },
          "composition_layers": {
            "type": "string",
            "enum": [
              "logo-arrow-text",
              "logo-text-arrow",
              "arrow-logo-text",
              "arrow-text-logo",
              "text-logo-arrow",
              "text-arrow-logo"
            ],
            "default": "logo-arrow-text",
            "description": "Paint order from back to front. The QR and artwork are never reordered."
          },
          "caption_rotation": {
            "type": "integer",
            "minimum": -180,
            "maximum": 180,
            "default": 0,
            "description": "Degrees, clockwise; -180 normalized to 180."
          },
          "caption_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "caption_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_scale": {
            "type": "integer",
            "minimum": 50,
            "maximum": 150,
            "default": 100,
            "description": "50..150 preset scale. For center, the visible longest side is never below20% or above30% of the complete QR, subject to safe module steps. Exterior presets retain their existing behavior."
          },
          "caption_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "arrow_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "color_mode": {
            "type": "string",
            "enum": [
              "solid",
              "linear",
              "radial"
            ],
            "default": "solid"
          },
          "gradient_color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Required for linear/radial. Both endpoints must pass contrast controls. Function patterns stay solid."
          },
          "gradient_angle": {
            "type": "integer",
            "minimum": 0,
            "maximum": 359,
            "default": 45,
            "description": "Clockwise, linear only."
          }
        }
      },
      "QrSummary": {
        "type": "object",
        "required": [
          "id",
          "label",
          "kind",
          "status",
          "campaign_id"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "label": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          }
        }
      },
      "QrCreatedReference": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "kind",
          "short_url",
          "status",
          "svg_url"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "kind": {
            "type": "string"
          },
          "short_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Stable short URL for dynamic codes; null for static codes."
          },
          "status": {
            "type": "string"
          },
          "svg_url": {
            "type": "string"
          }
        }
      },
      "QrAppearanceResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "style"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "style": {
            "$ref": "#/components/schemas/QrAppearance"
          }
        }
      },
      "QrStateResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "status"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          }
        }
      },
      "QrUpdateResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "status"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "starts_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "UTC instant."
          },
          "ends_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "UTC instant."
          },
          "timezone": {
            "type": "string",
            "enum": [
              "Europe/Paris",
              "Europe/London",
              "Europe/Berlin",
              "Europe/Rome",
              "Europe/Madrid",
              "Europe/Lisbon",
              "Europe/Bucharest",
              "Europe/Moscow",
              "Europe/Kyiv",
              "Europe/Amsterdam",
              "Europe/Warsaw",
              "Europe/Prague",
              "Europe/Bratislava",
              "Europe/Budapest",
              "Europe/Athens",
              "Europe/Sofia",
              "Europe/Zagreb",
              "Europe/Belgrade",
              "Europe/Istanbul",
              "Europe/Stockholm",
              "Europe/Copenhagen",
              "Europe/Helsinki",
              "Europe/Oslo",
              "UTC",
              "America/New_York",
              "America/Los_Angeles",
              "America/Sao_Paulo"
            ]
          }
        }
      },
      "QrImage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "destination"
        ],
        "properties": {
          "destination": {
            "type": "string",
            "format": "uri",
            "maxLength": 1000,
            "description": "HTTPS URL encoded verbatim; never fetched or persisted."
          },
          "format": {
            "type": "string",
            "enum": [
              "png",
              "jpeg"
            ],
            "default": "png"
          },
          "size": {
            "type": "integer",
            "enum": [
              512,
              1024,
              2048
            ],
            "default": 1024,
            "description": "Longest side in pixels. Original aspect ratio is preserved for complete designs."
          },
          "modules": {
            "type": "string",
            "enum": [
              "square",
              "dot",
              "rounded"
            ],
            "default": "square"
          },
          "finders": {
            "type": "string",
            "enum": [
              "square",
              "rounded",
              "circle"
            ],
            "default": "rounded"
          },
          "foreground": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#182B2A"
          },
          "background": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#FFFFFF"
          },
          "composition": {
            "type": "boolean",
            "default": false,
            "description": "Required true when template, caption, arrow or transparency is supplied. False returns the QR only."
          },
          "template": {
            "type": "string",
            "enum": [
              "plain",
              "card",
              "round",
              "seal",
              "corners",
              "bracket",
              "bubble",
              "tag",
              "ticket",
              "band",
              "polaroid",
              "heart",
              "arch",
              "fold",
              "capsule",
              "hexagon",
              "orbit",
              "ripple",
              "editorial",
              "rails",
              "ribbon",
              "pennant",
              "grid",
              "rosette",
              "car",
              "van",
              "bus",
              "bicycle",
              "rocket",
              "plane",
              "boat",
              "train",
              "cat",
              "dog",
              "bear",
              "fox",
              "rabbit",
              "owl",
              "butterfly",
              "flower",
              "leaf",
              "tree",
              "house",
              "camera",
              "phone",
              "laptop",
              "cup",
              "book",
              "bulb",
              "gift",
              "bottle",
              "lock",
              "gamepad",
              "football",
              "basketball",
              "music",
              "vinyl",
              "cinema",
              "palette",
              "crown",
              "guitar"
            ],
            "default": "plain",
            "description": "Approved template identifier; retired guitar maps to vinyl."
          },
          "caption": {
            "type": "string",
            "maxLength": 30,
            "nullable": true,
            "default": ""
          },
          "arrow": {
            "type": "string",
            "enum": [
              "none",
              "curve",
              "wave",
              "loop"
            ],
            "default": "none",
            "description": "Shown only with a non-empty caption."
          },
          "transparent": {
            "type": "boolean",
            "default": false,
            "description": "Only complete PNG designs. Quiet zone and QR background stay opaque."
          },
          "caption_font": {
            "type": "string",
            "enum": [
              "sans",
              "serif",
              "mono",
              "caveat",
              "patrick_hand"
            ],
            "description": "Caption font. Handwritten Caveat or Patrick Hand uses outlined glyphs in SVG/PDF and a bundled pinned font in PNG/JPEG. Supported fallback uses sans, unavailable glyphs use a missing-character box."
          },
          "logo_data": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 32768,
            "description": "Normalized PNG data URI, max384px per side and32768characters; empty removes. Center included in the QR-only image; header/callout require composition."
          },
          "caption_position": {
            "type": "string",
            "enum": [
              "auto",
              "top",
              "bottom",
              "left",
              "right"
            ],
            "default": "auto",
            "description": "Position du texte et de la flèche dans une bande extérieure réservée. auto conserve le modèle validé."
          },
          "caption_offset": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 50,
            "description": "Décalage horizontal (haut/bas) ou vertical (gauche/droite), en pourcentage de la plage sûre."
          },
          "caption_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Taille du texte extérieur, en pourcentage. Sans effet dans le placement auto."
          },
          "caption_align": {
            "type": "string",
            "enum": [
              "left",
              "center",
              "right"
            ],
            "default": "center"
          },
          "composition_mode": {
            "type": "string",
            "enum": [
              "legacy",
              "free"
            ],
            "default": "legacy",
            "description": "Any nondefault option or handwritten caption promotes free mode; auto placement becomes bottom. All editable elements remain in a protected exterior band."
          },
          "logo_mode": {
            "type": "string",
            "enum": [
              "header",
              "callout",
              "center"
            ],
            "default": "header",
            "description": "Center uses error correction H. The visible logo spans at least20% of the complete QR (quiet zone included), target25%, at most30%, preserving all functional patterns. A larger encoding version may be needed; unsuitable content or failed server decoding is refused. Center offsets must be zero."
          },
          "composition_layers": {
            "type": "string",
            "enum": [
              "logo-arrow-text",
              "logo-text-arrow",
              "arrow-logo-text",
              "arrow-text-logo",
              "text-logo-arrow",
              "text-arrow-logo"
            ],
            "default": "logo-arrow-text",
            "description": "Paint order from back to front. The QR and artwork are never reordered."
          },
          "caption_rotation": {
            "type": "integer",
            "minimum": -180,
            "maximum": 180,
            "default": 0,
            "description": "Degrees, clockwise; -180 normalized to 180."
          },
          "caption_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "caption_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_scale": {
            "type": "integer",
            "minimum": 50,
            "maximum": 150,
            "default": 100,
            "description": "50..150 preset scale. For center, the visible longest side is never below20% or above30% of the complete QR, subject to safe module steps. Exterior presets retain their existing behavior."
          },
          "caption_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "arrow_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "color_mode": {
            "type": "string",
            "enum": [
              "solid",
              "linear",
              "radial"
            ],
            "default": "solid"
          },
          "gradient_color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Required for linear/radial. Both endpoints must pass contrast controls. Function patterns stay solid."
          },
          "gradient_angle": {
            "type": "integer",
            "minimum": 0,
            "maximum": 359,
            "default": 45,
            "description": "Clockwise, linear only."
          }
        },
        "description": "Authenticated stateless rendering with images:render, no stored QR or statistics. Color gradients and normalized center logo allowed on QR-only output; other composition fields require composition=true. Center decoding, contrast, density and image budgets enforced."
      },
      "BatchRowInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "label",
          "destination"
        ],
        "properties": {
          "label": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "destination": {
            "type": "string",
            "maxLength": 1000,
            "description": "http/https URL. A malformed destination is reported as a row error at review."
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 128,
            "description": "Keep identifiers such as 00042 as strings."
          },
          "caption": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30
          },
          "filename": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 128
          }
        }
      },
      "BatchMapping": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "mapping",
          "revision"
        ],
        "properties": {
          "modules": {
            "enum": [
              "square",
              "rounded",
              "dot"
            ],
            "default": "square"
          },
          "finders": {
            "enum": [
              "square",
              "rounded",
              "circle"
            ],
            "default": "rounded"
          },
          "template": {
            "description": "Catalogue identifier. The retired guitar identifier remains accepted for compatibility and is normalized to vinyl; new integrations should use vinyl.",
            "enum": [
              "plain",
              "card",
              "round",
              "seal",
              "corners",
              "bracket",
              "bubble",
              "tag",
              "ticket",
              "band",
              "polaroid",
              "heart",
              "arch",
              "fold",
              "capsule",
              "hexagon",
              "orbit",
              "ripple",
              "editorial",
              "rails",
              "ribbon",
              "pennant",
              "grid",
              "rosette",
              "car",
              "van",
              "bus",
              "bicycle",
              "rocket",
              "plane",
              "boat",
              "train",
              "cat",
              "dog",
              "bear",
              "fox",
              "rabbit",
              "owl",
              "butterfly",
              "flower",
              "leaf",
              "tree",
              "house",
              "camera",
              "phone",
              "laptop",
              "cup",
              "book",
              "bulb",
              "gift",
              "bottle",
              "lock",
              "gamepad",
              "football",
              "basketball",
              "music",
              "vinyl",
              "cinema",
              "palette",
              "crown"
            ],
            "default": "plain"
          },
          "arrow": {
            "enum": [
              "none",
              "curve",
              "wave",
              "loop"
            ],
            "default": "none"
          },
          "foreground": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#182B2A"
          },
          "background": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#FFFFFF"
          },
          "caption": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30,
            "default": ""
          },
          "kind": {
            "type": "string",
            "enum": [
              "static",
              "dynamic"
            ],
            "default": "static"
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Campaign in the same workspace."
          },
          "mapping": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "label",
              "destination"
            ],
            "properties": {
              "label": {
                "type": "integer",
                "minimum": 0,
                "maximum": 31
              },
              "destination": {
                "type": "integer",
                "minimum": 0,
                "maximum": 31
              },
              "reference": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "maximum": 31
              },
              "caption": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "maximum": 31
              },
              "filename": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "maximum": 31
              }
            }
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 64,
            "maxLength": 64,
            "description": "null for uploaded state; otherwise latest revision returned by review."
          },
          "caption_font": {
            "type": "string",
            "enum": [
              "sans",
              "serif",
              "mono",
              "caveat",
              "patrick_hand"
            ],
            "description": "Caption font. Handwritten Caveat or Patrick Hand uses outlined glyphs in SVG/PDF and a bundled pinned font in PNG/JPEG. Supported fallback uses sans, unavailable glyphs use a missing-character box."
          },
          "logo_data": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 32768,
            "description": "Normalized PNG data URI, max384px per side and32768characters; empty removes. Center included in the QR-only image; header/callout require composition."
          },
          "caption_position": {
            "type": "string",
            "enum": [
              "auto",
              "top",
              "bottom",
              "left",
              "right"
            ],
            "default": "auto",
            "description": "Position du texte et de la flèche dans une bande extérieure réservée. auto conserve le modèle validé."
          },
          "caption_offset": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 50,
            "description": "Décalage horizontal (haut/bas) ou vertical (gauche/droite), en pourcentage de la plage sûre."
          },
          "caption_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Taille du texte extérieur, en pourcentage. Sans effet dans le placement auto."
          },
          "caption_align": {
            "type": "string",
            "enum": [
              "left",
              "center",
              "right"
            ],
            "default": "center"
          },
          "composition_mode": {
            "type": "string",
            "enum": [
              "legacy",
              "free"
            ],
            "default": "legacy",
            "description": "Any nondefault option or handwritten caption promotes free mode; auto placement becomes bottom. All editable elements remain in a protected exterior band."
          },
          "logo_mode": {
            "type": "string",
            "enum": [
              "header",
              "callout",
              "center"
            ],
            "default": "header",
            "description": "Center uses error correction H. The visible logo spans at least20% of the complete QR (quiet zone included), target25%, at most30%, preserving all functional patterns. A larger encoding version may be needed; unsuitable content or failed server decoding is refused. Center offsets must be zero."
          },
          "composition_layers": {
            "type": "string",
            "enum": [
              "logo-arrow-text",
              "logo-text-arrow",
              "arrow-logo-text",
              "arrow-text-logo",
              "text-logo-arrow",
              "text-arrow-logo"
            ],
            "default": "logo-arrow-text",
            "description": "Paint order from back to front. The QR and artwork are never reordered."
          },
          "caption_rotation": {
            "type": "integer",
            "minimum": -180,
            "maximum": 180,
            "default": 0,
            "description": "Degrees, clockwise; -180 normalized to 180."
          },
          "caption_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "caption_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_scale": {
            "type": "integer",
            "minimum": 50,
            "maximum": 150,
            "default": 100,
            "description": "50..150 preset scale. For center, the visible longest side is never below20% or above30% of the complete QR, subject to safe module steps. Exterior presets retain their existing behavior."
          },
          "caption_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "arrow_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "color_mode": {
            "type": "string",
            "enum": [
              "solid",
              "linear",
              "radial"
            ],
            "default": "solid"
          },
          "gradient_color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Required for linear/radial. Both endpoints must pass contrast controls. Function patterns stay solid."
          },
          "gradient_angle": {
            "type": "integer",
            "minimum": 0,
            "maximum": 359,
            "default": 45,
            "description": "Clockwise, linear only."
          }
        }
      },
      "BatchJsonInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "rows"
        ],
        "properties": {
          "modules": {
            "enum": [
              "square",
              "rounded",
              "dot"
            ],
            "default": "square"
          },
          "finders": {
            "enum": [
              "square",
              "rounded",
              "circle"
            ],
            "default": "rounded"
          },
          "template": {
            "description": "Catalogue identifier. The retired guitar identifier remains accepted for compatibility and is normalized to vinyl; new integrations should use vinyl.",
            "enum": [
              "plain",
              "card",
              "round",
              "seal",
              "corners",
              "bracket",
              "bubble",
              "tag",
              "ticket",
              "band",
              "polaroid",
              "heart",
              "arch",
              "fold",
              "capsule",
              "hexagon",
              "orbit",
              "ripple",
              "editorial",
              "rails",
              "ribbon",
              "pennant",
              "grid",
              "rosette",
              "car",
              "van",
              "bus",
              "bicycle",
              "rocket",
              "plane",
              "boat",
              "train",
              "cat",
              "dog",
              "bear",
              "fox",
              "rabbit",
              "owl",
              "butterfly",
              "flower",
              "leaf",
              "tree",
              "house",
              "camera",
              "phone",
              "laptop",
              "cup",
              "book",
              "bulb",
              "gift",
              "bottle",
              "lock",
              "gamepad",
              "football",
              "basketball",
              "music",
              "vinyl",
              "cinema",
              "palette",
              "crown"
            ],
            "default": "plain"
          },
          "arrow": {
            "enum": [
              "none",
              "curve",
              "wave",
              "loop"
            ],
            "default": "none"
          },
          "foreground": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#182B2A"
          },
          "background": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#FFFFFF"
          },
          "caption": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30,
            "default": ""
          },
          "kind": {
            "type": "string",
            "enum": [
              "static",
              "dynamic"
            ],
            "default": "static"
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Campaign in the same workspace."
          },
          "rows": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/BatchRowInput"
            }
          },
          "caption_font": {
            "type": "string",
            "enum": [
              "sans",
              "serif",
              "mono",
              "caveat",
              "patrick_hand"
            ],
            "description": "Caption font. Handwritten Caveat or Patrick Hand uses outlined glyphs in SVG/PDF and a bundled pinned font in PNG/JPEG. Supported fallback uses sans, unavailable glyphs use a missing-character box."
          },
          "logo_data": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 32768,
            "description": "Normalized PNG data URI, max384px per side and32768characters; empty removes. Center included in the QR-only image; header/callout require composition."
          },
          "caption_position": {
            "type": "string",
            "enum": [
              "auto",
              "top",
              "bottom",
              "left",
              "right"
            ],
            "default": "auto",
            "description": "Position du texte et de la flèche dans une bande extérieure réservée. auto conserve le modèle validé."
          },
          "caption_offset": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 50,
            "description": "Décalage horizontal (haut/bas) ou vertical (gauche/droite), en pourcentage de la plage sûre."
          },
          "caption_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Taille du texte extérieur, en pourcentage. Sans effet dans le placement auto."
          },
          "caption_align": {
            "type": "string",
            "enum": [
              "left",
              "center",
              "right"
            ],
            "default": "center"
          },
          "composition_mode": {
            "type": "string",
            "enum": [
              "legacy",
              "free"
            ],
            "default": "legacy",
            "description": "Any nondefault option or handwritten caption promotes free mode; auto placement becomes bottom. All editable elements remain in a protected exterior band."
          },
          "logo_mode": {
            "type": "string",
            "enum": [
              "header",
              "callout",
              "center"
            ],
            "default": "header",
            "description": "Center uses error correction H. The visible logo spans at least20% of the complete QR (quiet zone included), target25%, at most30%, preserving all functional patterns. A larger encoding version may be needed; unsuitable content or failed server decoding is refused. Center offsets must be zero."
          },
          "composition_layers": {
            "type": "string",
            "enum": [
              "logo-arrow-text",
              "logo-text-arrow",
              "arrow-logo-text",
              "arrow-text-logo",
              "text-logo-arrow",
              "text-arrow-logo"
            ],
            "default": "logo-arrow-text",
            "description": "Paint order from back to front. The QR and artwork are never reordered."
          },
          "caption_rotation": {
            "type": "integer",
            "minimum": -180,
            "maximum": 180,
            "default": 0,
            "description": "Degrees, clockwise; -180 normalized to 180."
          },
          "caption_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "caption_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_scale": {
            "type": "integer",
            "minimum": 50,
            "maximum": 150,
            "default": 100,
            "description": "50..150 preset scale. For center, the visible longest side is never below20% or above30% of the complete QR, subject to safe module steps. Exterior presets retain their existing behavior."
          },
          "caption_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "arrow_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "color_mode": {
            "type": "string",
            "enum": [
              "solid",
              "linear",
              "radial"
            ],
            "default": "solid"
          },
          "gradient_color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Required for linear/radial. Both endpoints must pass contrast controls. Function patterns stay solid."
          },
          "gradient_angle": {
            "type": "integer",
            "minimum": 0,
            "maximum": 359,
            "default": 45,
            "description": "Clockwise, linear only."
          }
        }
      },
      "BatchFileInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "file"
        ],
        "properties": {
          "modules": {
            "enum": [
              "square",
              "rounded",
              "dot"
            ],
            "default": "square"
          },
          "finders": {
            "enum": [
              "square",
              "rounded",
              "circle"
            ],
            "default": "rounded"
          },
          "template": {
            "description": "Catalogue identifier. The retired guitar identifier remains accepted for compatibility and is normalized to vinyl; new integrations should use vinyl.",
            "enum": [
              "plain",
              "card",
              "round",
              "seal",
              "corners",
              "bracket",
              "bubble",
              "tag",
              "ticket",
              "band",
              "polaroid",
              "heart",
              "arch",
              "fold",
              "capsule",
              "hexagon",
              "orbit",
              "ripple",
              "editorial",
              "rails",
              "ribbon",
              "pennant",
              "grid",
              "rosette",
              "car",
              "van",
              "bus",
              "bicycle",
              "rocket",
              "plane",
              "boat",
              "train",
              "cat",
              "dog",
              "bear",
              "fox",
              "rabbit",
              "owl",
              "butterfly",
              "flower",
              "leaf",
              "tree",
              "house",
              "camera",
              "phone",
              "laptop",
              "cup",
              "book",
              "bulb",
              "gift",
              "bottle",
              "lock",
              "gamepad",
              "football",
              "basketball",
              "music",
              "vinyl",
              "cinema",
              "palette",
              "crown"
            ],
            "default": "plain"
          },
          "arrow": {
            "enum": [
              "none",
              "curve",
              "wave",
              "loop"
            ],
            "default": "none"
          },
          "foreground": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#182B2A"
          },
          "background": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#FFFFFF"
          },
          "caption": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30,
            "default": ""
          },
          "kind": {
            "type": "string",
            "enum": [
              "static",
              "dynamic"
            ],
            "default": "static"
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Campaign in the same workspace."
          },
          "file": {
            "type": "string",
            "format": "binary",
            "description": "CSV or XLSX, maximum 1 MB. XLSX first worksheet only, formulas never evaluated."
          },
          "mapping": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "label",
              "destination"
            ],
            "properties": {
              "label": {
                "type": "integer",
                "minimum": 0,
                "maximum": 31
              },
              "destination": {
                "type": "integer",
                "minimum": 0,
                "maximum": 31
              },
              "reference": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "maximum": 31
              },
              "caption": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "maximum": 31
              },
              "filename": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "maximum": 31
              }
            }
          },
          "caption_font": {
            "type": "string",
            "enum": [
              "sans",
              "serif",
              "mono",
              "caveat",
              "patrick_hand"
            ],
            "description": "Caption font. Handwritten Caveat or Patrick Hand uses outlined glyphs in SVG/PDF and a bundled pinned font in PNG/JPEG. Supported fallback uses sans, unavailable glyphs use a missing-character box."
          },
          "logo_data": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 32768,
            "description": "Normalized PNG data URI, max384px per side and32768characters; empty removes. Center included in the QR-only image; header/callout require composition."
          },
          "caption_position": {
            "type": "string",
            "enum": [
              "auto",
              "top",
              "bottom",
              "left",
              "right"
            ],
            "default": "auto",
            "description": "Position du texte et de la flèche dans une bande extérieure réservée. auto conserve le modèle validé."
          },
          "caption_offset": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 50,
            "description": "Décalage horizontal (haut/bas) ou vertical (gauche/droite), en pourcentage de la plage sûre."
          },
          "caption_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Taille du texte extérieur, en pourcentage. Sans effet dans le placement auto."
          },
          "caption_align": {
            "type": "string",
            "enum": [
              "left",
              "center",
              "right"
            ],
            "default": "center"
          },
          "composition_mode": {
            "type": "string",
            "enum": [
              "legacy",
              "free"
            ],
            "default": "legacy",
            "description": "Any nondefault option or handwritten caption promotes free mode; auto placement becomes bottom. All editable elements remain in a protected exterior band."
          },
          "logo_mode": {
            "type": "string",
            "enum": [
              "header",
              "callout",
              "center"
            ],
            "default": "header",
            "description": "Center uses error correction H. The visible logo spans at least20% of the complete QR (quiet zone included), target25%, at most30%, preserving all functional patterns. A larger encoding version may be needed; unsuitable content or failed server decoding is refused. Center offsets must be zero."
          },
          "composition_layers": {
            "type": "string",
            "enum": [
              "logo-arrow-text",
              "logo-text-arrow",
              "arrow-logo-text",
              "arrow-text-logo",
              "text-logo-arrow",
              "text-arrow-logo"
            ],
            "default": "logo-arrow-text",
            "description": "Paint order from back to front. The QR and artwork are never reordered."
          },
          "caption_rotation": {
            "type": "integer",
            "minimum": -180,
            "maximum": 180,
            "default": 0,
            "description": "Degrees, clockwise; -180 normalized to 180."
          },
          "caption_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "caption_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_scale": {
            "type": "integer",
            "minimum": 50,
            "maximum": 150,
            "default": 100,
            "description": "50..150 preset scale. For center, the visible longest side is never below20% or above30% of the complete QR, subject to safe module steps. Exterior presets retain their existing behavior."
          },
          "caption_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "arrow_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "color_mode": {
            "type": "string",
            "enum": [
              "solid",
              "linear",
              "radial"
            ],
            "default": "solid"
          },
          "gradient_color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Required for linear/radial. Both endpoints must pass contrast controls. Function patterns stay solid."
          },
          "gradient_angle": {
            "type": "integer",
            "minimum": 0,
            "maximum": 359,
            "default": 45,
            "description": "Clockwise, linear only."
          }
        },
        "description": "Without mapping, only file is accepted; apply style at PATCH mapping. With mapping, import and review are atomic."
      },
      "BatchSummary": {
        "type": "object",
        "required": [
          "total",
          "valid",
          "invalid",
          "created"
        ],
        "properties": {
          "total": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "valid": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "invalid": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "created": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          }
        }
      },
      "Batch": {
        "type": "object",
        "required": [
          "id",
          "status",
          "revision",
          "expires_at",
          "completed_at",
          "summary"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "uploaded",
              "review",
              "completed",
              "cancelled"
            ]
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 64,
            "maxLength": 64
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "summary": {
            "$ref": "#/components/schemas/BatchSummary"
          },
          "headers": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Column names only. Raw unselected cells are never returned."
          },
          "mapping": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "label",
                  "destination"
                ],
                "properties": {
                  "label": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 31
                  },
                  "destination": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 31
                  },
                  "reference": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 31
                  },
                  "caption": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 31
                  },
                  "filename": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 31
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "style": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "modules": {
                "enum": [
                  "square",
                  "rounded",
                  "dot"
                ],
                "default": "square"
              },
              "finders": {
                "enum": [
                  "square",
                  "rounded",
                  "circle"
                ],
                "default": "rounded"
              },
              "template": {
                "description": "Catalogue identifier. The retired guitar identifier remains accepted for compatibility and is normalized to vinyl; new integrations should use vinyl.",
                "enum": [
                  "plain",
                  "card",
                  "round",
                  "seal",
                  "corners",
                  "bracket",
                  "bubble",
                  "tag",
                  "ticket",
                  "band",
                  "polaroid",
                  "heart",
                  "arch",
                  "fold",
                  "capsule",
                  "hexagon",
                  "orbit",
                  "ripple",
                  "editorial",
                  "rails",
                  "ribbon",
                  "pennant",
                  "grid",
                  "rosette",
                  "car",
                  "van",
                  "bus",
                  "bicycle",
                  "rocket",
                  "plane",
                  "boat",
                  "train",
                  "cat",
                  "dog",
                  "bear",
                  "fox",
                  "rabbit",
                  "owl",
                  "butterfly",
                  "flower",
                  "leaf",
                  "tree",
                  "house",
                  "camera",
                  "phone",
                  "laptop",
                  "cup",
                  "book",
                  "bulb",
                  "gift",
                  "bottle",
                  "lock",
                  "gamepad",
                  "football",
                  "basketball",
                  "music",
                  "vinyl",
                  "cinema",
                  "palette",
                  "crown"
                ],
                "default": "plain"
              },
              "arrow": {
                "enum": [
                  "none",
                  "curve",
                  "wave",
                  "loop"
                ],
                "default": "none"
              },
              "foreground": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$",
                "default": "#182B2A"
              },
              "background": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$",
                "default": "#FFFFFF"
              },
              "caption": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 30,
                "default": ""
              },
              "kind": {
                "type": "string",
                "enum": [
                  "static",
                  "dynamic"
                ],
                "default": "static"
              },
              "campaign_id": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 1,
                "description": "Campaign in the same workspace."
              }
            }
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "number": {
                  "type": "integer",
                  "minimum": 1
                },
                "data": {
                  "$ref": "#/components/schemas/BatchRowInput"
                },
                "errors": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Stable machine codes from the web batch validator, such as invalid_destination, duplicate_reference, formula."
                },
                "code_id": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "in_collection": {
                  "type": "boolean"
                }
              }
            }
          }
        },
        "description": "Detailed headers/mapping/style/rows only with batches:read. List responses and write-only responses contain only the summary. completed created counts remain historical when codes are trashed; in_collection reflects current presence."
      },
      "HistoryVersion": {
        "type": "object",
        "required": [
          "id",
          "number",
          "changes",
          "source",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": "integer",
            "minimum": 1
          },
          "changes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "name",
                "content",
                "design"
              ]
            }
          },
          "source": {
            "type": "string",
            "enum": [
              "web",
              "api",
              "system",
              "restore"
            ]
          },
          "restored_from": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Source version identifier; it may later be pruned."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "HistoryRevision": {
        "type": "string",
        "pattern": "^[a-f0-9]{64}$",
        "description": "Opaque current revision from history. Includes the latest version, identity and current access settings; changes invalidate it."
      },
      "StyleKitInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "modules",
          "finders",
          "template",
          "arrow",
          "foreground",
          "background",
          "caption"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Trimmed, no control characters."
          },
          "modules": {
            "enum": [
              "square",
              "rounded",
              "dot"
            ],
            "default": "square"
          },
          "finders": {
            "enum": [
              "square",
              "rounded",
              "circle"
            ],
            "default": "square"
          },
          "template": {
            "description": "Catalogue identifier. The retired guitar identifier remains accepted for compatibility and is normalized to vinyl; new integrations should use vinyl.",
            "enum": [
              "plain",
              "card",
              "round",
              "seal",
              "corners",
              "bracket",
              "bubble",
              "tag",
              "ticket",
              "band",
              "polaroid",
              "heart",
              "arch",
              "fold",
              "capsule",
              "hexagon",
              "orbit",
              "ripple",
              "editorial",
              "rails",
              "ribbon",
              "pennant",
              "grid",
              "rosette",
              "car",
              "van",
              "bus",
              "bicycle",
              "rocket",
              "plane",
              "boat",
              "train",
              "cat",
              "dog",
              "bear",
              "fox",
              "rabbit",
              "owl",
              "butterfly",
              "flower",
              "leaf",
              "tree",
              "house",
              "camera",
              "phone",
              "laptop",
              "cup",
              "book",
              "bulb",
              "gift",
              "bottle",
              "lock",
              "gamepad",
              "football",
              "basketball",
              "music",
              "vinyl",
              "cinema",
              "palette",
              "crown"
            ],
            "default": "plain"
          },
          "arrow": {
            "enum": [
              "none",
              "curve",
              "wave",
              "loop"
            ],
            "default": "none"
          },
          "foreground": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#182B2A"
          },
          "background": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#FFFFFF"
          },
          "caption": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30,
            "default": "",
            "pattern": "^[^\\u0000-\\u001F\\u007F]*$"
          },
          "caption_font": {
            "type": "string",
            "enum": [
              "sans",
              "serif",
              "mono",
              "caveat",
              "patrick_hand"
            ],
            "description": "Caption font. Handwritten Caveat or Patrick Hand uses outlined glyphs in SVG/PDF and a bundled pinned font in PNG/JPEG. Supported fallback uses sans, unavailable glyphs use a missing-character box."
          },
          "logo_data": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 32768,
            "description": "Normalized PNG data URI, max384px per side and32768characters; empty removes. Center included in the QR-only image; header/callout require composition."
          },
          "caption_position": {
            "type": "string",
            "enum": [
              "auto",
              "top",
              "bottom",
              "left",
              "right"
            ],
            "default": "auto",
            "description": "Position du texte et de la flèche dans une bande extérieure réservée. auto conserve le modèle validé."
          },
          "caption_offset": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 50,
            "description": "Décalage horizontal (haut/bas) ou vertical (gauche/droite), en pourcentage de la plage sûre."
          },
          "caption_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Taille du texte extérieur, en pourcentage. Sans effet dans le placement auto."
          },
          "caption_align": {
            "type": "string",
            "enum": [
              "left",
              "center",
              "right"
            ],
            "default": "center"
          },
          "composition_mode": {
            "type": "string",
            "enum": [
              "legacy",
              "free"
            ],
            "default": "legacy",
            "description": "Any nondefault option or handwritten caption promotes free mode; auto placement becomes bottom. All editable elements remain in a protected exterior band."
          },
          "logo_mode": {
            "type": "string",
            "enum": [
              "header",
              "callout",
              "center"
            ],
            "default": "header",
            "description": "Center uses error correction H. The visible logo spans at least20% of the complete QR (quiet zone included), target25%, at most30%, preserving all functional patterns. A larger encoding version may be needed; unsuitable content or failed server decoding is refused. Center offsets must be zero."
          },
          "composition_layers": {
            "type": "string",
            "enum": [
              "logo-arrow-text",
              "logo-text-arrow",
              "arrow-logo-text",
              "arrow-text-logo",
              "text-logo-arrow",
              "text-arrow-logo"
            ],
            "default": "logo-arrow-text",
            "description": "Paint order from back to front. The QR and artwork are never reordered."
          },
          "caption_rotation": {
            "type": "integer",
            "minimum": -180,
            "maximum": 180,
            "default": 0,
            "description": "Degrees, clockwise; -180 normalized to 180."
          },
          "caption_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "caption_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_scale": {
            "type": "integer",
            "minimum": 50,
            "maximum": 150,
            "default": 100,
            "description": "50..150 preset scale. For center, the visible longest side is never below20% or above30% of the complete QR, subject to safe module steps. Exterior presets retain their existing behavior."
          },
          "caption_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "arrow_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "color_mode": {
            "type": "string",
            "enum": [
              "solid",
              "linear",
              "radial"
            ],
            "default": "solid"
          },
          "gradient_color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Required for linear/radial. Both endpoints must pass contrast controls. Function patterns stay solid."
          },
          "gradient_angle": {
            "type": "integer",
            "minimum": 0,
            "maximum": 359,
            "default": 45,
            "description": "Clockwise, linear only."
          }
        }
      },
      "StyleKitUpdate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "modules",
          "finders",
          "template",
          "arrow",
          "foreground",
          "background",
          "caption",
          "revision"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Trimmed, no control characters."
          },
          "modules": {
            "enum": [
              "square",
              "rounded",
              "dot"
            ],
            "default": "square"
          },
          "finders": {
            "enum": [
              "square",
              "rounded",
              "circle"
            ],
            "default": "square"
          },
          "template": {
            "description": "Catalogue identifier. The retired guitar identifier remains accepted for compatibility and is normalized to vinyl; new integrations should use vinyl.",
            "enum": [
              "plain",
              "card",
              "round",
              "seal",
              "corners",
              "bracket",
              "bubble",
              "tag",
              "ticket",
              "band",
              "polaroid",
              "heart",
              "arch",
              "fold",
              "capsule",
              "hexagon",
              "orbit",
              "ripple",
              "editorial",
              "rails",
              "ribbon",
              "pennant",
              "grid",
              "rosette",
              "car",
              "van",
              "bus",
              "bicycle",
              "rocket",
              "plane",
              "boat",
              "train",
              "cat",
              "dog",
              "bear",
              "fox",
              "rabbit",
              "owl",
              "butterfly",
              "flower",
              "leaf",
              "tree",
              "house",
              "camera",
              "phone",
              "laptop",
              "cup",
              "book",
              "bulb",
              "gift",
              "bottle",
              "lock",
              "gamepad",
              "football",
              "basketball",
              "music",
              "vinyl",
              "cinema",
              "palette",
              "crown"
            ],
            "default": "plain"
          },
          "arrow": {
            "enum": [
              "none",
              "curve",
              "wave",
              "loop"
            ],
            "default": "none"
          },
          "foreground": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#182B2A"
          },
          "background": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#FFFFFF"
          },
          "caption": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 30,
            "default": "",
            "pattern": "^[^\\u0000-\\u001F\\u007F]*$"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "caption_font": {
            "type": "string",
            "enum": [
              "sans",
              "serif",
              "mono",
              "caveat",
              "patrick_hand"
            ],
            "description": "Caption font. Handwritten Caveat or Patrick Hand uses outlined glyphs in SVG/PDF and a bundled pinned font in PNG/JPEG. Supported fallback uses sans, unavailable glyphs use a missing-character box."
          },
          "logo_data": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 32768,
            "description": "Normalized PNG data URI, max384px per side and32768characters; empty removes. Center included in the QR-only image; header/callout require composition."
          },
          "caption_position": {
            "type": "string",
            "enum": [
              "auto",
              "top",
              "bottom",
              "left",
              "right"
            ],
            "default": "auto",
            "description": "Position du texte et de la flèche dans une bande extérieure réservée. auto conserve le modèle validé."
          },
          "caption_offset": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 50,
            "description": "Décalage horizontal (haut/bas) ou vertical (gauche/droite), en pourcentage de la plage sûre."
          },
          "caption_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Taille du texte extérieur, en pourcentage. Sans effet dans le placement auto."
          },
          "caption_align": {
            "type": "string",
            "enum": [
              "left",
              "center",
              "right"
            ],
            "default": "center"
          },
          "composition_mode": {
            "type": "string",
            "enum": [
              "legacy",
              "free"
            ],
            "default": "legacy",
            "description": "Any nondefault option or handwritten caption promotes free mode; auto placement becomes bottom. All editable elements remain in a protected exterior band."
          },
          "logo_mode": {
            "type": "string",
            "enum": [
              "header",
              "callout",
              "center"
            ],
            "default": "header",
            "description": "Center uses error correction H. The visible logo spans at least20% of the complete QR (quiet zone included), target25%, at most30%, preserving all functional patterns. A larger encoding version may be needed; unsuitable content or failed server decoding is refused. Center offsets must be zero."
          },
          "composition_layers": {
            "type": "string",
            "enum": [
              "logo-arrow-text",
              "logo-text-arrow",
              "arrow-logo-text",
              "arrow-text-logo",
              "text-logo-arrow",
              "text-arrow-logo"
            ],
            "default": "logo-arrow-text",
            "description": "Paint order from back to front. The QR and artwork are never reordered."
          },
          "caption_rotation": {
            "type": "integer",
            "minimum": -180,
            "maximum": 180,
            "default": 0,
            "description": "Degrees, clockwise; -180 normalized to 180."
          },
          "caption_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "caption_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "arrow_scale": {
            "type": "integer",
            "minimum": 80,
            "maximum": 140,
            "default": 100,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dx": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_dy": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "default": 0,
            "description": "Percentage within the safe range; translation never covers the QR."
          },
          "logo_scale": {
            "type": "integer",
            "minimum": 50,
            "maximum": 150,
            "default": 100,
            "description": "50..150 preset scale. For center, the visible longest side is never below20% or above30% of the complete QR, subject to safe module steps. Exterior presets retain their existing behavior."
          },
          "caption_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "arrow_color": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Optional; empty/null inherits foreground. Contrast against background must be >= 4.5. Inherited color is checked for visible advanced text/arrow."
          },
          "color_mode": {
            "type": "string",
            "enum": [
              "solid",
              "linear",
              "radial"
            ],
            "default": "solid"
          },
          "gradient_color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "description": "Required for linear/radial. Both endpoints must pass contrast controls. Function patterns stay solid."
          },
          "gradient_angle": {
            "type": "integer",
            "minimum": 0,
            "maximum": 359,
            "default": 45,
            "description": "Clockwise, linear only."
          }
        }
      },
      "StyleKit": {
        "type": "object",
        "required": [
          "id",
          "name",
          "revision",
          "style",
          "archived",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "style": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "modules": {
                "enum": [
                  "square",
                  "rounded",
                  "dot"
                ],
                "default": "square"
              },
              "finders": {
                "enum": [
                  "square",
                  "rounded",
                  "circle"
                ],
                "default": "square"
              },
              "template": {
                "description": "Catalogue identifier. The retired guitar identifier remains accepted for compatibility and is normalized to vinyl; new integrations should use vinyl.",
                "enum": [
                  "plain",
                  "card",
                  "round",
                  "seal",
                  "corners",
                  "bracket",
                  "bubble",
                  "tag",
                  "ticket",
                  "band",
                  "polaroid",
                  "heart",
                  "arch",
                  "fold",
                  "capsule",
                  "hexagon",
                  "orbit",
                  "ripple",
                  "editorial",
                  "rails",
                  "ribbon",
                  "pennant",
                  "grid",
                  "rosette",
                  "car",
                  "van",
                  "bus",
                  "bicycle",
                  "rocket",
                  "plane",
                  "boat",
                  "train",
                  "cat",
                  "dog",
                  "bear",
                  "fox",
                  "rabbit",
                  "owl",
                  "butterfly",
                  "flower",
                  "leaf",
                  "tree",
                  "house",
                  "camera",
                  "phone",
                  "laptop",
                  "cup",
                  "book",
                  "bulb",
                  "gift",
                  "bottle",
                  "lock",
                  "gamepad",
                  "football",
                  "basketball",
                  "music",
                  "vinyl",
                  "cinema",
                  "palette",
                  "crown"
                ],
                "default": "plain"
              },
              "arrow": {
                "enum": [
                  "none",
                  "curve",
                  "wave",
                  "loop"
                ],
                "default": "none"
              },
              "foreground": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$",
                "default": "#182B2A"
              },
              "background": {
                "type": "string",
                "pattern": "^#[0-9a-fA-F]{6}$",
                "default": "#FFFFFF"
              },
              "caption": {
                "type": [
                  "string",
                  "null"
                ],
                "maxLength": 30,
                "default": "",
                "pattern": "^[^\\u0000-\\u001F\\u007F]*$"
              }
            },
            "required": [
              "modules",
              "finders",
              "template",
              "arrow",
              "foreground",
              "background",
              "caption"
            ]
          },
          "archived": {
            "type": "boolean"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "brand": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "display_name": {
                "type": "string",
                "maxLength": 60
              },
              "signature": {
                "type": "string",
                "maxLength": 100
              },
              "font": {
                "type": "string",
                "enum": [
                  "sans",
                  "serif",
                  "mono"
                ]
              },
              "has_logo": {
                "type": "boolean"
              }
            }
          }
        },
        "additionalProperties": false
      },
      "StyleKitReference": {
        "type": "object",
        "required": [
          "id",
          "revision",
          "archived"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "archived": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "HostedPageBlock": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "type"
        ],
        "description": "Stable UUID id may be omitted on save. Fields outside the selected type are discarded. Plain text only. HTTP(S) URLs without credentials. text requires body; link requires title and URL; menu_item requires title; event requires title, start and timezone. End must be later; non-existent local daylight-saving times are rejected. image requires an owned immutable image asset and nonempty alt; document requires an owned PDF asset, title and accessible summary in body. Only an asset belonging to this page and matching the block type is accepted. form requires title, purpose, scale, contact and a nonempty plain-text privacy notice. Non-feedback purposes always normalize scale to none. Signup requires optional contact collection; its public email field is required. Ratings are stored as integers 1..10 (half-star units); null is unrated. Publication and page entitlements apply to collection. Gallery: title and 2..6 own images with alt; FAQ: title and 1..8 complete questions/answers; blank editor rows ignored, half-complete rows refused, UUIDs distinct after lowercase normalization. Callout: title/body and info/highlight/warning. Video/audio: title and HTTPS link, optional owned image with alt, explicit click only, no external player/autoplay. Nested media references stay protected by current publication/password/grants and history. Plain text only.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "link",
              "menu_item",
              "event",
              "image",
              "document",
              "form",
              "gallery",
              "faq",
              "callout",
              "video",
              "audio"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 120
          },
          "body": {
            "type": "string",
            "maxLength": 2000
          },
          "url": {
            "type": "string",
            "maxLength": 1000,
            "format": "uri"
          },
          "price": {
            "type": "string",
            "maxLength": 80
          },
          "allergens": {
            "type": "string",
            "maxLength": 240
          },
          "start": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$"
          },
          "end": {
            "type": "string",
            "maxLength": 16
          },
          "timezone": {
            "type": "string",
            "description": "An IANA zone from the existing catalog scheduling timezones."
          },
          "location": {
            "type": "string",
            "maxLength": 240
          },
          "asset_id": {
            "type": "string",
            "format": "uuid"
          },
          "alt": {
            "type": "string",
            "maxLength": 240
          },
          "purpose": {
            "type": "string",
            "enum": [
              "feedback",
              "quote",
              "signup",
              "report"
            ]
          },
          "scale": {
            "type": "string",
            "enum": [
              "half",
              "whole",
              "none"
            ]
          },
          "contact": {
            "type": "string",
            "enum": [
              "optional",
              "none"
            ]
          },
          "privacy": {
            "type": "string",
            "minLength": 1,
            "maxLength": 800
          },
          "items": {
            "type": "array",
            "maxItems": 8,
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/GalleryItem"
                },
                {
                  "$ref": "#/components/schemas/FaqItem"
                }
              ]
            }
          },
          "tone": {
            "type": "string",
            "enum": [
              "info",
              "highlight",
              "warning"
            ]
          },
          "media_asset_id": {
            "type": "string",
            "format": "uuid",
            "description": "Owned audio/video normalized file, alternative to external URL; must belong to this page."
          },
          "caption_asset_id": {
            "type": "string",
            "format": "uuid",
            "description": "Owned UTF8 WebVTT, required for hosted video with audio; cues <= media duration+1s."
          },
          "transcript": {
            "type": "string",
            "maxLength": 12000,
            "description": "Required for hosted audio; plain text only."
          },
          "retention_days": {
            "type": "integer",
            "enum": [
              30,
              90,
              180
            ],
            "default": 180,
            "description": "Future responses after publication only; existing expiry unchanged."
          },
          "attachments_enabled": {
            "type": "boolean",
            "default": false,
            "description": "Optional single PNG/JPEG/PDF <=2MiB, forms except signup; private encrypted storage."
          }
        }
      },
      "HostedPageVariant": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "title",
          "intro",
          "blocks"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 120
          },
          "intro": {
            "type": "string",
            "maxLength": 800
          },
          "blocks": {
            "type": "array",
            "maxItems": 40,
            "items": {
              "$ref": "#/components/schemas/HostedPageBlock"
            }
          }
        }
      },
      "HostedPageSnapshot": {
        "type": "object",
        "required": [
          "default_locale",
          "brand",
          "variants"
        ],
        "properties": {
          "default_locale": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "brand": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "maxLength": 80
              },
              "accent": {
                "type": "string",
                "pattern": "^#[0-9A-Fa-f]{6}$",
                "description": "Contrast at least 4.5:1 against white and the fixed paper background (#f7f5ef)."
              },
              "font": {
                "type": "string",
                "enum": [
                  "sans",
                  "serif",
                  "mono"
                ]
              },
              "signature": {
                "type": "string",
                "maxLength": 100
              },
              "logo_asset_id": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uuid"
              }
            }
          },
          "variants": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/HostedPageVariant"
            }
          }
        },
        "description": "Total normalized snapshot at most 128 KiB; no automatic translation. Only variants explicitly saved and published are public."
      },
      "HostedPage": {
        "type": "object",
        "required": [
          "id",
          "qr_code_id",
          "type",
          "revision",
          "published_version_id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "qr_code_id": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "enum": [
              "links",
              "menu",
              "guide",
              "product",
              "event",
              "app",
              "article"
            ]
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "published_version_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "draft": {
            "$ref": "#/components/schemas/HostedPageSnapshot"
          },
          "preview_token": {
            "type": "string",
            "minLength": 64,
            "maxLength": 64
          }
        },
        "description": "Index returns metadata only. Detail requires pages:read. Mutation response includes draft and preview_token only if the key also has pages:read. Preview token binds actor, workspace, page and revision."
      },
      "HostedPageSave": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "locale",
          "default_locale",
          "accent",
          "title",
          "blocks"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "locale": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "default_locale": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "brand_name": {
            "type": "string",
            "maxLength": 80
          },
          "accent": {
            "type": "string",
            "pattern": "^#[0-9A-Fa-f]{6}$"
          },
          "title": {
            "type": "string",
            "maxLength": 120
          },
          "intro": {
            "type": "string",
            "maxLength": 800
          },
          "blocks": {
            "type": "array",
            "maxItems": 40,
            "items": {
              "$ref": "#/components/schemas/HostedPageBlock"
            }
          },
          "brand_font": {
            "type": "string",
            "enum": [
              "sans",
              "serif",
              "mono"
            ],
            "description": "Omit to preserve current page font."
          },
          "brand_signature": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100
          },
          "brand_logo_asset_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Immutable image in this page only, <=256KiB. Null explicitly removes the logo."
          },
          "brand_kit_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Explicit copy into the draft, same workspace, extra kits:read required. Copies an independent local logo file. No live link, QR drawing change or publication."
          }
        },
        "description": "Saves exactly one language and common brand. Other languages and published snapshot stay unchanged. default_locale must exist after save. Raw request and total normalized draft are bounded at 128 KiB. Web form body allowance is 1 MiB for percent-encoding; API raw JSON and canonical UTF-8 snapshot remain bounded to 128 KiB."
      },
      "HostedPagePublication": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "preview_token",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "preview_token": {
            "type": "string",
            "minLength": 64,
            "maxLength": 64
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        },
        "description": "Use token and revision returned by GET /pages/{page}. Publishes all saved variants after validation, not only the current locale. Same immediate retry is idempotent; changed revision fails 409. Publication and withdrawal retain up to 30 immutable versions."
      },
      "HostedPageRevision": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "HostedPageMedia": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "document",
              "audio",
              "video",
              "captions"
            ]
          },
          "mime": {
            "type": "string",
            "enum": [
              "image/png",
              "image/jpeg",
              "application/pdf",
              "audio/mpeg",
              "video/mp4",
              "text/vtt"
            ]
          },
          "bytes": {
            "type": "integer"
          },
          "width": {
            "type": [
              "integer",
              "null"
            ]
          },
          "height": {
            "type": [
              "integer",
              "null"
            ]
          },
          "pages": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "duration_ms": {
                "type": "integer"
              },
              "has_audio": {
                "type": "boolean"
              },
              "cue_count": {
                "type": "integer"
              },
              "last_cue_ms": {
                "type": "integer"
              }
            }
          }
        },
        "description": "Private source filenames, paths, hashes and originals never returned. Immutable normalized file; public only while referenced by the current published page variant."
      },
      "FeedbackEntry": {
        "type": "object",
        "additionalProperties": false,
        "description": "Private personal/company response. Retained for 30/90/180 days according to the published form; existing expiration remains unchanged, encrypted at rest. Visitor contact is declared and unverified; no marketing consent or email dispatch is implied. Raw message/contact data is returned only with feedback:read and current manager permissions. Explicit public ack_requested checkbox opts into a generic receipt without echoing private content; maximum one per address per UTC day and 50 per workspace/day, no newsletter subscription. Visitor address is not verified by a receipt.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "page_id": {
            "type": "string",
            "format": "uuid"
          },
          "qr_code_id": {
            "type": "integer"
          },
          "locale": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "purpose": {
            "type": "string",
            "enum": [
              "feedback",
              "quote",
              "signup",
              "report"
            ]
          },
          "rating_units": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 10
          },
          "status": {
            "type": "string",
            "enum": [
              "new",
              "in_progress",
              "resolved"
            ]
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "email_verified": {
            "type": "boolean",
            "const": false
          },
          "details": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "form_title": {
                "type": "string",
                "maxLength": 120
              },
              "privacy": {
                "type": "string",
                "maxLength": 800
              },
              "brand_name": {
                "type": "string",
                "maxLength": 80
              },
              "message": {
                "type": "string",
                "maxLength": 2000
              },
              "first_name": {
                "type": "string",
                "maxLength": 80
              },
              "last_name": {
                "type": "string",
                "maxLength": 80
              },
              "company": {
                "type": "string",
                "maxLength": 120
              },
              "email": {
                "type": "string",
                "maxLength": 254
              }
            }
          },
          "replies": {
            "type": "array",
            "maxItems": 10,
            "description": "Present on the detail endpoint; requires feedback:read.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "queued",
                    "sending",
                    "sent",
                    "blocked",
                    "uncertain"
                  ]
                },
                "message": {
                  "type": "string",
                  "maxLength": 2000
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "sent_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "files": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/FeedbackFile"
            }
          }
        }
      },
      "FeedbackUpdate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "status"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "new",
              "in_progress",
              "resolved"
            ]
          }
        }
      },
      "FeedbackDelete": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "FeedbackReply": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        },
        "required": [
          "revision",
          "request_id",
          "message",
          "confirmed"
        ],
        "description": "Explicit mail to the email declared on this response; no arbitrary to/from/header field. Current editor or manager rights required. Reuse request_id and identical message for idempotent retries. Changed payload or stale revision: 409. Maximum 10 replies per entry, 3 per recipient and 50 per workspace per UTC day. Responses follow the entry retention, encrypted at rest. SMTP uncertainty is never automatically retried."
      },
      "FeedbackReplyResource": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "sending",
              "sent",
              "blocked",
              "uncertain"
            ]
          }
        },
        "required": [
          "id",
          "status"
        ]
      },
      "FeedbackMailPreference": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "off",
              "instant",
              "daily",
              "weekly"
            ]
          },
          "timezone": {
            "type": "string",
            "maxLength": 64,
            "example": "Europe/Paris"
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "next_digest_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "FeedbackMailPreferenceUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "off",
              "instant",
              "daily",
              "weekly"
            ]
          },
          "timezone": {
            "type": "string",
            "maxLength": 64,
            "example": "Europe/Paris"
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        },
        "required": [
          "mode",
          "timezone"
        ],
        "description": "Self only, current verified account address; company/personal boundary comes from the API key. Omit/null revision only for first creation, otherwise current revision required. Complete IANA timezone identifier. Changing settings invalidates pending notifications. Daily at 08:00 local, weekly Monday 08:00; aggregation counts new retained entries, rated count separately, no copied private message. Instant maximum 20/day. Off by default."
      },
      "RestaurantOptionInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "label"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Omit for first save to generate UUID; keep existing UUID for translations/updates."
          },
          "label": {
            "type": "string",
            "maxLength": 100
          },
          "price": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,5})([.,][0-9]{1,2})?$",
            "description": "Exact decimal amount (comma or dot); parsed without floating point. Alternative: price_cents integer. Do not send both."
          },
          "price_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 99999999
          }
        },
        "oneOf": [
          {
            "required": [
              "price"
            ],
            "not": {
              "required": [
                "price_cents"
              ]
            }
          },
          {
            "required": [
              "price_cents"
            ],
            "not": {
              "required": [
                "price"
              ]
            }
          }
        ]
      },
      "RestaurantItemInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "available",
          "max_quantity"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Omit for first save to generate UUID; keep existing UUID for translations/updates."
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "category": {
            "type": "string",
            "maxLength": 80
          },
          "description": {
            "type": "string",
            "maxLength": 1000
          },
          "allergens": {
            "type": "string",
            "maxLength": 240
          },
          "price": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,5})([.,][0-9]{1,2})?$",
            "description": "Exact decimal amount (comma or dot); parsed without floating point. Alternative: price_cents integer. Do not send both."
          },
          "price_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 99999999
          },
          "available": {
            "type": "boolean"
          },
          "max_quantity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20
          },
          "stock": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 9999,
            "description": "Base item units per service. Null unlimited; zero unavailable. Only confirmed/completed requests consume availability."
          },
          "options": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "$ref": "#/components/schemas/RestaurantOptionInput"
            }
          }
        },
        "oneOf": [
          {
            "required": [
              "price"
            ],
            "not": {
              "required": [
                "price_cents"
              ]
            }
          },
          {
            "required": [
              "price_cents"
            ],
            "not": {
              "required": [
                "price"
              ]
            }
          }
        ]
      },
      "RestaurantSlotInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "date",
          "time",
          "capacity",
          "enabled"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Omit for first save to generate UUID; keep existing UUID for translations/updates."
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "time": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$"
          },
          "duration_minutes": {
            "type": "integer",
            "minimum": 15,
            "maximum": 480,
            "default": 120
          },
          "capacity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500
          },
          "enabled": {
            "type": "boolean"
          }
        },
        "description": "Dated service in menu IANA timezone, within 90 days on publication. Enabled windows cannot overlap; closed services with confirmed bookings also block overlap. Published confirmed service times/durations cannot change; capacity cannot go below reservations."
      },
      "RestaurantMenuInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "locale",
          "currency",
          "timezone",
          "privacy",
          "terms",
          "items",
          "slots",
          "require_preorder",
          "lead_minutes",
          "cancel_hours",
          "max_guests"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Required current revision when updating; null only first save."
          },
          "locale": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "currency": {
            "type": "string",
            "enum": [
              "EUR",
              "GBP",
              "CHF",
              "PLN",
              "RON",
              "SEK",
              "NOK",
              "DKK",
              "CZK",
              "HUF",
              "BGN",
              "RSD",
              "TRY",
              "RUB",
              "UAH"
            ]
          },
          "timezone": {
            "type": "string",
            "maxLength": 64
          },
          "require_preorder": {
            "type": "boolean"
          },
          "lead_minutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10080
          },
          "cancel_hours": {
            "type": "integer",
            "minimum": 0,
            "maximum": 168
          },
          "max_guests": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          },
          "privacy": {
            "type": "string",
            "maxLength": 800
          },
          "terms": {
            "type": "string",
            "maxLength": 1200
          },
          "items": {
            "type": "array",
            "maxItems": 40,
            "items": {
              "$ref": "#/components/schemas/RestaurantItemInput"
            }
          },
          "slots": {
            "type": "array",
            "maxItems": 120,
            "items": {
              "$ref": "#/components/schemas/RestaurantSlotInput"
            }
          }
        },
        "description": "Writable flat locale-specific input. Stable UUIDs preserve other language texts on save. Structural changes/removal affect the complete menu. Canonical draft/published responses use locale maps and price_cents; do not PUT those responses unchanged. Combined canonical menu <=128 KiB; normal JSON API transport, HTML editor batches fields as catalogue_json to avoid PHP input limits."
      },
      "RestaurantSelection": {
        "type": "array",
        "maxItems": 40,
        "items": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "id",
            "quantity"
          ],
          "properties": {
            "id": {
              "type": "string",
              "format": "uuid"
            },
            "quantity": {
              "type": "integer",
              "minimum": 0,
              "maximum": 20
            },
            "options": {
              "type": "array",
              "maxItems": 6,
              "uniqueItems": true,
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          }
        }
      },
      "RestaurantRequestInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "menu_id",
          "menu_revision",
          "request_id",
          "locale",
          "selection",
          "slot_id",
          "guests",
          "name",
          "email",
          "confirmed"
        ],
        "properties": {
          "menu_id": {
            "type": "string",
            "format": "uuid"
          },
          "menu_revision": {
            "type": "string",
            "format": "uuid"
          },
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "locale": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "selection": {
            "$ref": "#/components/schemas/RestaurantSelection"
          },
          "slot_id": {
            "type": "string",
            "format": "uuid"
          },
          "guests": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "email": {
            "type": "string",
            "maxLength": 254,
            "format": "email"
          },
          "note": {
            "type": "string",
            "maxLength": 1200
          },
          "email_followup": {
            "type": "boolean",
            "default": false
          },
          "confirmed": {
            "const": true
          }
        },
        "description": "<=32 KiB. Trusted workspace integration only; caller obtains explicit customer agreement. No caller-supplied totals, plan, workspace owner or status. Always creates pending. request_id deduplicates per menu for 180 days: semantic identical retry returns existing minimal record; changed command 409; erased record returns data:null without recreating private details. This is not an order/payment API."
      },
      "RestaurantDecision": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "action",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "enum": [
              "confirm",
              "propose",
              "decline",
              "cancel",
              "complete"
            ]
          },
          "confirmed": {
            "const": true
          },
          "slot_id": {
            "type": "string",
            "format": "uuid"
          },
          "message": {
            "type": "string",
            "maxLength": 1200
          }
        },
        "description": "<=8 KiB. slot_id required only for propose and forbidden otherwise. confirm: pending only, current quote unchanged, capacity/stock checked under lock. propose refreshes the same chosen item/option IDs at current prices/allergens on selected service. Explicit visitor approval returns pending; a further manual confirmation is required. complete only after service starts; cancel frees capacity. Existing revisions are rejected with 409."
      },
      "RestaurantPublication": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "preview_token",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "preview_token": {
            "type": "string",
            "minLength": 64,
            "maxLength": 64
          },
          "confirmed": {
            "const": true
          },
          "notification_mode": {
            "type": "string",
            "enum": [
              "keep",
              "me",
              "off"
            ]
          }
        },
        "description": "Preview token bound to actor/page/menu revisions. Publish requires all published page language variants translated. me uses current verified operator, never arbitrary recipient. Withdraw stops new requests without deleting follow-up."
      },
      "RestaurantRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "menu_id": {
            "type": "string",
            "format": "uuid"
          },
          "page_id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "proposed",
              "confirmed",
              "declined",
              "cancelled",
              "completed"
            ]
          },
          "slot_id": {
            "type": "string",
            "format": "uuid"
          },
          "guests": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          },
          "starts_at": {
            "type": "string",
            "format": "date-time"
          },
          "confirmed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "email_verified": {
            "type": "boolean"
          },
          "details": {
            "type": "object",
            "description": "Read scope only: frozen quote with integer prices, recipe/allergens, customer contact, opt-in, terms and history. Never includes access_secret or fingerprints."
          }
        },
        "description": "Encrypted private storage, retained 180 days; no access token, private email link or session secret in API output. Read requires requests_read; write returns only id/status/revision. Account analysts cannot access contacts."
      },
      "JourneyOptionInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "label"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Omit for first save to generate UUID; keep existing UUID for translations/updates."
          },
          "label": {
            "type": "string",
            "maxLength": 100
          },
          "price": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,5})([.,][0-9]{1,2})?$",
            "description": "Exact decimal amount (comma or dot); parsed without floating point. Alternative: price_cents integer. Do not send both."
          },
          "price_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 99999999
          }
        },
        "oneOf": [
          {
            "required": [
              "price"
            ],
            "not": {
              "required": [
                "price_cents"
              ]
            }
          },
          {
            "required": [
              "price_cents"
            ],
            "not": {
              "required": [
                "price"
              ]
            }
          }
        ]
      },
      "JourneyItemInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "available",
          "max_quantity"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Omit for first save to generate UUID; keep existing UUID for translations/updates."
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "category": {
            "type": "string",
            "maxLength": 80
          },
          "description": {
            "type": "string",
            "maxLength": 1000
          },
          "allergens": {
            "type": "string",
            "maxLength": 240
          },
          "price": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,5})([.,][0-9]{1,2})?$",
            "description": "Exact decimal amount (comma or dot); parsed without floating point. Alternative: price_cents integer. Do not send both."
          },
          "price_cents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 99999999
          },
          "available": {
            "type": "boolean"
          },
          "max_quantity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20
          },
          "stock": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 9999,
            "description": "Base item units per service. Null unlimited; zero unavailable. Only confirmed/completed requests consume availability."
          },
          "options": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "$ref": "#/components/schemas/JourneyOptionInput"
            }
          }
        },
        "oneOf": [
          {
            "required": [
              "price"
            ],
            "not": {
              "required": [
                "price_cents"
              ]
            }
          },
          {
            "required": [
              "price_cents"
            ],
            "not": {
              "required": [
                "price"
              ]
            }
          }
        ],
        "description": "Empty price means a quote is required; explicit zero is a free offer. Exact integer minor units otherwise. One Shopify variant per item, without options when commerce is configured."
      },
      "JourneySlotInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "date",
          "time",
          "capacity",
          "enabled"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Omit for first save to generate UUID; keep existing UUID for translations/updates."
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "time": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$"
          },
          "duration_minutes": {
            "type": "integer",
            "minimum": 15,
            "maximum": 480,
            "default": 120
          },
          "capacity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500
          },
          "enabled": {
            "type": "boolean"
          }
        },
        "description": "Dated service in menu IANA timezone, within 90 days on publication. Enabled windows cannot overlap; closed services with confirmed bookings also block overlap. Published confirmed service times/durations cannot change; capacity cannot go below reservations."
      },
      "JourneyMenuInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "locale",
          "currency",
          "timezone",
          "privacy",
          "terms",
          "items",
          "slots",
          "require_preorder",
          "lead_minutes",
          "cancel_hours",
          "max_guests",
          "profession_id",
          "mode"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Required current revision when updating; null only first save."
          },
          "locale": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "currency": {
            "type": "string",
            "enum": [
              "EUR",
              "GBP",
              "CHF",
              "PLN",
              "RON",
              "SEK",
              "NOK",
              "DKK",
              "CZK",
              "HUF",
              "BGN",
              "RSD",
              "TRY",
              "RUB",
              "UAH"
            ]
          },
          "timezone": {
            "type": "string",
            "maxLength": 64
          },
          "require_preorder": {
            "type": "boolean"
          },
          "lead_minutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10080
          },
          "cancel_hours": {
            "type": "integer",
            "minimum": 0,
            "maximum": 168
          },
          "max_guests": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          },
          "privacy": {
            "type": "string",
            "maxLength": 800
          },
          "terms": {
            "type": "string",
            "maxLength": 1200
          },
          "items": {
            "type": "array",
            "maxItems": 40,
            "items": {
              "$ref": "#/components/schemas/JourneyItemInput"
            }
          },
          "slots": {
            "type": "array",
            "maxItems": 120,
            "items": {
              "$ref": "#/components/schemas/JourneySlotInput"
            }
          },
          "profession_id": {
            "type": "string",
            "enum": [
              "catering",
              "hotels",
              "camping",
              "shops",
              "ecommerce",
              "crafts",
              "property",
              "automotive",
              "logistics",
              "health",
              "beauty",
              "sport",
              "education",
              "associations",
              "tourism",
              "agriculture",
              "wine",
              "events",
              "music",
              "galleries",
              "consulting",
              "municipalities",
              "maintenance"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "appointment",
              "pickup",
              "quote",
              "request",
              "report"
            ]
          },
          "commerce": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "shop": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9-]{0,62}\\.myshopify\\.com$"
              },
              "variants": {
                "type": "object",
                "maxProperties": 40,
                "additionalProperties": {
                  "type": "string",
                  "pattern": "^[1-9][0-9]{0,19}$"
                }
              }
            }
          }
        },
        "description": "Writable flat locale-specific input. Stable UUIDs preserve other language texts on save. Structural changes/removal affect the complete menu. Canonical draft/published responses use locale maps and price_cents; do not PUT those responses unchanged. Combined canonical menu <=128 KiB; normal JSON API transport, HTML editor batches fields as catalogue_json to avoid PHP input limits."
      },
      "JourneySelection": {
        "type": "array",
        "maxItems": 40,
        "items": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "id",
            "quantity"
          ],
          "properties": {
            "id": {
              "type": "string",
              "format": "uuid"
            },
            "quantity": {
              "type": "integer",
              "minimum": 0,
              "maximum": 20
            },
            "options": {
              "type": "array",
              "maxItems": 6,
              "uniqueItems": true,
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          }
        }
      },
      "JourneyRequestInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "menu_id",
          "menu_revision",
          "request_id",
          "locale",
          "selection",
          "guests",
          "name",
          "email",
          "confirmed"
        ],
        "properties": {
          "menu_id": {
            "type": "string",
            "format": "uuid"
          },
          "menu_revision": {
            "type": "string",
            "format": "uuid"
          },
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "locale": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "selection": {
            "$ref": "#/components/schemas/JourneySelection"
          },
          "slot_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "guests": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "email": {
            "type": "string",
            "maxLength": 254,
            "format": "email"
          },
          "note": {
            "type": "string",
            "maxLength": 1200
          },
          "email_followup": {
            "type": "boolean",
            "default": false
          },
          "confirmed": {
            "const": true
          },
          "context": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "reference": {
                "type": "string",
                "maxLength": 240
              },
              "place": {
                "type": "string",
                "maxLength": 240
              },
              "dimensions": {
                "type": "string",
                "maxLength": 240
              },
              "level": {
                "type": "string",
                "maxLength": 240
              }
            }
          }
        },
        "description": "A scheduled profile requires slot_id. Otherwise omit it, and guests must be 1. Context fields are restricted by profession. No health information, note or extra fields on the health profile. request_id is namespaced by catalogue."
      },
      "JourneyDecision": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "action",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "enum": [
              "confirm",
              "propose",
              "decline",
              "cancel",
              "complete"
            ]
          },
          "confirmed": {
            "const": true
          },
          "slot_id": {
            "type": "string",
            "format": "uuid"
          },
          "message": {
            "type": "string",
            "maxLength": 1200
          }
        },
        "description": "Manual confirmation only. Undated proposals omit slot_id. After customer acceptance the request returns to pending, requiring another explicit confirmation."
      },
      "JourneyPublication": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "preview_token",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "preview_token": {
            "type": "string",
            "minLength": 64,
            "maxLength": 64
          },
          "confirmed": {
            "const": true
          },
          "notification_mode": {
            "type": "string",
            "enum": [
              "keep",
              "me",
              "off"
            ]
          }
        },
        "description": "Preview token bound to actor/page/menu revisions. Publish requires all published page language variants translated. me uses current verified operator, never arbitrary recipient. Withdraw stops new requests without deleting follow-up."
      },
      "JourneyRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "menu_id": {
            "type": "string",
            "format": "uuid"
          },
          "page_id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "proposed",
              "confirmed",
              "declined",
              "cancelled",
              "completed"
            ]
          },
          "slot_id": {
            "type": "string",
            "format": "uuid"
          },
          "guests": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          },
          "starts_at": {
            "type": "string",
            "format": "date-time"
          },
          "confirmed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "email_verified": {
            "type": "boolean"
          },
          "details": {
            "type": "object",
            "description": "Read scope only: frozen quote with integer prices, recipe/allergens, customer contact, opt-in, terms and history. Never includes access_secret or fingerprints."
          }
        },
        "description": "Encrypted private storage, retained 180 days; no access token, private email link or session secret in API output. Read requires requests_read; write returns only id/status/revision. Account analysts cannot access contacts."
      },
      "AgencyClient": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 1
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "quota": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100000
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "archived": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "name",
          "quota",
          "revision",
          "archived"
        ]
      },
      "AgencyClientCreate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "quota": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100000
          }
        },
        "required": [
          "request_id",
          "name",
          "quota"
        ]
      },
      "AgencyClientUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "quota": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100000
          },
          "archived": {
            "type": "boolean"
          }
        },
        "required": [
          "revision",
          "name",
          "quota",
          "archived"
        ]
      },
      "ClientReviewCreate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "code_id": {
            "type": "integer",
            "minimum": 1
          },
          "reviewer_id": {
            "type": "integer",
            "minimum": 1
          }
        },
        "required": [
          "request_id",
          "code_id",
          "reviewer_id"
        ]
      },
      "ClientReviewDecision": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "decision": {
            "type": "string",
            "enum": [
              "approved",
              "changes_requested"
            ]
          },
          "comment": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1000
          }
        },
        "required": [
          "revision",
          "decision"
        ]
      },
      "DomainCreate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "host": {
            "type": "string",
            "maxLength": 253
          }
        },
        "required": [
          "host"
        ]
      },
      "Revision": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "revision"
        ]
      },
      "AgencyClientAdopt": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "request_id",
          "company_id",
          "quota"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "company_id": {
            "type": "integer",
            "minimum": 1
          },
          "quota": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100000
          }
        }
      },
      "AgencyEligibleCompany": {
        "type": "object",
        "required": [
          "id",
          "name",
          "used"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 1
          },
          "name": {
            "type": "string"
          },
          "used": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "OwnershipTransfer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "company_id": {
            "type": "integer"
          },
          "from_user_id": {
            "type": "integer"
          },
          "to_user_id": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "accepted",
              "declined",
              "cancelled",
              "expired"
            ]
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "source_confirmed": {
            "type": "boolean"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "summary": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "codes": {
                "type": "integer",
                "description": "Occupied non-trashed QR slots, including disabled or expired codes."
              },
              "trash": {
                "type": "integer"
              },
              "pages": {
                "type": "integer"
              },
              "locations": {
                "type": "integer"
              },
              "members": {
                "type": "integer"
              }
            }
          },
          "acceptance_url": {
            "type": "string",
            "format": "uri"
          },
          "acceptance_requires": {
            "type": "string",
            "const": "both_accounts_authenticated_session_password_and_two_factor"
          }
        }
      },
      "CollectionReviewCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "request_id",
          "campaign_id",
          "reviewer_id"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "campaign_id": {
            "type": "integer",
            "minimum": 1
          },
          "reviewer_id": {
            "type": "integer",
            "minimum": 1
          }
        },
        "example": {
          "request_id": "2cda0142-afd8-46ed-b108-973a0e68bbf9",
          "campaign_id": 42,
          "reviewer_id": 17
        }
      },
      "CollectionReviewDecision": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "decision",
          "consent"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "decision": {
            "type": "string",
            "enum": [
              "approved",
              "changes_requested"
            ]
          },
          "consent": {
            "type": "boolean",
            "const": true
          },
          "comment": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 1000
          }
        },
        "example": {
          "revision": "5ef89f3c-71ef-4275-ad59-fc73972525dc",
          "decision": "approved",
          "consent": true,
          "comment": "All artwork approved."
        }
      },
      "CollectionReview": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "campaign_id": {
            "type": "integer"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "changes_requested"
            ]
          },
          "count": {
            "type": "integer",
            "minimum": 0,
            "maximum": 25
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked": {
            "type": "boolean"
          },
          "deliveries": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "CollectionChangeCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "request_id",
          "codes"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "codes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "uniqueItems": true,
            "items": {
              "type": "integer",
              "minimum": 1
            }
          },
          "kit_id": {
            "type": "integer",
            "minimum": 1
          },
          "destination": {
            "type": "string",
            "format": "uri",
            "maxLength": 1000
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "schedule": {
            "type": "object",
            "minProperties": 1,
            "additionalProperties": false,
            "properties": {
              "starts_at": {
                "type": [
                  "string",
                  "null"
                ],
                "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$"
              },
              "ends_at": {
                "type": [
                  "string",
                  "null"
                ],
                "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$"
              },
              "timezone": {
                "type": "string",
                "enum": [
                  "Europe/Paris",
                  "Europe/London",
                  "Europe/Berlin",
                  "Europe/Rome",
                  "Europe/Madrid",
                  "Europe/Lisbon",
                  "Europe/Bucharest",
                  "Europe/Moscow",
                  "Europe/Kyiv",
                  "Europe/Amsterdam",
                  "Europe/Warsaw",
                  "Europe/Prague",
                  "Europe/Bratislava",
                  "Europe/Budapest",
                  "Europe/Athens",
                  "Europe/Sofia",
                  "Europe/Zagreb",
                  "Europe/Belgrade",
                  "Europe/Istanbul",
                  "Europe/Stockholm",
                  "Europe/Copenhagen",
                  "Europe/Helsinki",
                  "Europe/Oslo",
                  "UTC",
                  "America/New_York",
                  "America/Los_Angeles",
                  "America/Sao_Paulo"
                ]
              },
              "fallback_url": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri",
                "maxLength": 1000
              }
            }
          }
        },
        "anyOf": [
          {
            "required": [
              "kit_id"
            ]
          },
          {
            "required": [
              "destination"
            ]
          },
          {
            "required": [
              "campaign_id"
            ]
          },
          {
            "required": [
              "schedule"
            ]
          }
        ],
        "example": {
          "request_id": "fe292e98-639b-46ab-b0c9-e5f957cf8587",
          "codes": [
            41,
            42
          ],
          "kit_id": 5
        }
      },
      "CollectionChangeDecision": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "action",
          "consent"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "enum": [
              "apply",
              "undo",
              "cancel"
            ]
          },
          "consent": {
            "type": "boolean",
            "const": true
          }
        },
        "example": {
          "revision": "d0672bc4-cf2f-4a62-b354-fe2ca188e3e1",
          "action": "apply",
          "consent": true
        }
      },
      "CollectionChange": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "preview",
              "applied",
              "undone",
              "cancelled"
            ]
          },
          "count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "undo_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "results": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            }
          }
        }
      },
      "QrAccess": {
        "type": "object",
        "required": [
          "enabled",
          "configured",
          "revision",
          "session_minutes"
        ],
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "configured": {
            "type": "boolean"
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "session_minutes": {
            "type": "integer",
            "const": 60
          }
        }
      },
      "QrAccessUpdate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "enabled"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "access_password": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 12,
            "maxLength": 72,
            "description": "At most 72 UTF-8 bytes; no bcrypt truncation. Omit to keep current secret. Required when enabling without a configured secret. Never returned."
          }
        }
      },
      "RoutingRule": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "label",
          "target",
          "days"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "label": {
            "type": "string",
            "maxLength": 80
          },
          "target": {
            "type": "string",
            "format": "uri",
            "maxLength": 1000,
            "pattern": "^https?://",
            "description": "No credentials or control characters; never fetched by QRissimo."
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              null,
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "device": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              null,
              "desktop",
              "mobile",
              "tablet"
            ]
          },
          "days": {
            "type": "array",
            "uniqueItems": true,
            "maxItems": 7,
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 7
            },
            "description": "ISO weekdays 1=Monday. Empty means any day; overnight windows belong to their starting day."
          },
          "start": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$"
          },
          "end": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$"
          }
        }
      },
      "RoutingExperiment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "a",
          "b",
          "weight_a"
        ],
        "properties": {
          "a": {
            "type": "string",
            "format": "uri",
            "maxLength": 1000,
            "pattern": "^https?://",
            "description": "No credentials or control characters; never fetched by QRissimo."
          },
          "b": {
            "type": "string",
            "format": "uri",
            "maxLength": 1000,
            "pattern": "^https?://",
            "description": "No credentials or control characters; never fetched by QRissimo."
          },
          "weight_a": {
            "type": "integer",
            "minimum": 1,
            "maximum": 99
          }
        }
      },
      "RoutingConfiguration": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "timezone",
          "rules"
        ],
        "properties": {
          "timezone": {
            "type": "string",
            "example": "Europe/Paris"
          },
          "fallback": {
            "anyOf": [
              {
                "type": "string",
                "format": "uri",
                "maxLength": 1000,
                "pattern": "^https?://",
                "description": "No credentials or control characters; never fetched by QRissimo."
              },
              {
                "type": "null"
              }
            ]
          },
          "rules": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/RoutingRule"
            }
          },
          "experiment": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RoutingExperiment"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "QrRouting": {
        "type": "object",
        "required": [
          "revision",
          "enabled",
          "configuration"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "configuration": {
            "$ref": "#/components/schemas/RoutingConfiguration"
          }
        }
      },
      "QrRoutingUpdate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "timezone",
          "rules",
          "revision",
          "enabled"
        ],
        "properties": {
          "timezone": {
            "type": "string",
            "example": "Europe/Paris"
          },
          "fallback": {
            "anyOf": [
              {
                "type": "string",
                "format": "uri",
                "maxLength": 1000,
                "pattern": "^https?://",
                "description": "No credentials or control characters; never fetched by QRissimo."
              },
              {
                "type": "null"
              }
            ]
          },
          "rules": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/RoutingRule"
            }
          },
          "experiment": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RoutingExperiment"
              },
              {
                "type": "null"
              }
            ]
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          }
        }
      },
      "RoutingSimulation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "language",
          "device",
          "at",
          "bucket"
        ],
        "properties": {
          "language": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "device": {
            "type": "string",
            "enum": [
              "desktop",
              "mobile",
              "tablet"
            ]
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-13T13:00:00+02:00"
          },
          "bucket": {
            "type": "integer",
            "minimum": 0,
            "maximum": 99
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Optional current revision; stale value is 409."
          }
        }
      },
      "ConversionSettings": {
        "type": "object",
        "required": [
          "revision",
          "enabled",
          "parameter",
          "compatible",
          "token_hours",
          "max_pending"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "parameter": {
            "type": "string",
            "pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,31}$"
          },
          "compatible": {
            "type": "boolean"
          },
          "token_hours": {
            "type": "integer",
            "const": 24
          },
          "max_pending": {
            "type": "integer",
            "const": 10000
          }
        }
      },
      "ConversionSettingsUpdate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "enabled",
          "parameter"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "parameter": {
            "type": "string",
            "pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,31}$"
          }
        }
      },
      "ConversionConfirmation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "token",
          "external_id",
          "kind",
          "confirmed"
        ],
        "properties": {
          "token": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$",
            "description": "Opaque attribution value received at the destination, never an API key."
          },
          "external_id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_.:-]{1,100}$",
            "description": "Stable event identifier unique in the workspace; reuse unchanged on retries."
          },
          "kind": {
            "type": "string",
            "enum": [
              "lead",
              "signup",
              "booking",
              "purchase",
              "participation"
            ]
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "ConversionReceipt": {
        "type": "object",
        "required": [
          "id",
          "confirmed_at",
          "replayed"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed_at": {
            "type": "string",
            "format": "date-time"
          },
          "replayed": {
            "type": "boolean"
          }
        }
      },
      "ConversionReport": {
        "type": "object",
        "properties": {
          "days": {
            "type": "integer",
            "const": 30
          },
          "attributed_openings": {
            "type": "integer",
            "minimum": 0
          },
          "converted_openings": {
            "type": "integer",
            "minimum": 0
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": [
                    "lead",
                    "signup",
                    "booking",
                    "purchase",
                    "participation"
                  ]
                },
                "conversions": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "routing_revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "variants": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "kind": {
                  "type": "string"
                },
                "choice": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "label": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "attributed_openings": {
                  "type": "integer",
                  "minimum": 1
                },
                "converted_openings": {
                  "type": "integer",
                  "minimum": 0
                },
                "rate_percent": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100
                }
              }
            }
          }
        }
      },
      "PassConfiguration": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "kind",
          "enabled",
          "valid_from",
          "valid_until",
          "timezone"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "ticket",
              "coupon"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "valid_from": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with seconds and explicit offset or Z."
          },
          "valid_until": {
            "type": "string",
            "format": "date-time",
            "description": "Exclusive expiry, after start or now if start is null."
          },
          "timezone": {
            "type": "string",
            "description": "Supported IANA zone from the QRissimo catalog; dates are stored in UTC."
          }
        }
      },
      "PassRedemption": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "page_version",
          "request_id",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "page_version": {
            "type": "string",
            "format": "uuid"
          },
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "PassRevoke": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "PassState": {
        "type": "object",
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "ticket",
              "coupon"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "valid_from": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601 with seconds and explicit offset or Z."
          },
          "valid_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Exclusive expiry, after start or now if start is null."
          },
          "timezone": {
            "type": "string",
            "description": "Supported IANA zone from the QRissimo catalog; dates are stored in UTC."
          },
          "state": {
            "type": "string",
            "enum": [
              "draft",
              "paused",
              "unpublished",
              "upcoming",
              "ready",
              "expired",
              "used",
              "revoked",
              "unavailable"
            ]
          },
          "page_version": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "page_id": {
            "type": "string",
            "format": "uuid"
          },
          "receipt": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "redeemed_at": {
                "type": "string",
                "format": "date-time"
              },
              "redeemed_by": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          },
          "published_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 120
          },
          "published_intro": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 800
          },
          "code_label": {
            "type": "string",
            "maxLength": 255
          },
          "code_revision": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$"
          },
          "location_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "PassReceipt": {
        "type": "object",
        "required": [
          "id",
          "redeemed_at",
          "replayed"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "redeemed_at": {
            "type": "string",
            "format": "date-time"
          },
          "replayed": {
            "type": "boolean"
          }
        }
      },
      "PassListItem": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PassState"
          },
          {
            "type": "object",
            "properties": {
              "code_id": {
                "type": "integer"
              },
              "label": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        ]
      },
      "AssetConfiguration": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "page_version",
          "kind",
          "enabled",
          "reference",
          "next_due",
          "timezone",
          "public_reference",
          "public_due"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "page_version": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "equipment",
              "product"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "reference": {
            "type": "string",
            "maxLength": 80
          },
          "sku": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80
          },
          "serial": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80
          },
          "lot": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80
          },
          "manufacturer": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 120
          },
          "shelf": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80
          },
          "next_due": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "timezone": {
            "type": "string",
            "maxLength": 64
          },
          "public_reference": {
            "type": "boolean"
          },
          "public_due": {
            "type": "boolean"
          }
        },
        "description": "Full configuration, owner/admin with current confirmed 2FA and actual server entitlement. Null revision only before first configuration. Reference unique within workspace, NFC normalized and case-insensitive; duplicate SKU is allowed. Optional details omitted here become empty. UTC due date requires seconds and explicit offset, years 1000..9999. Public reference exposes reference and SKU (SKU only on product), never serial/lot/shelf/notes. Technical history limit 5000 entries, with one terminal retirement still allowed."
      },
      "AssetCheckInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "request_id",
          "kind",
          "note",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "inspection",
              "maintenance",
              "issue"
            ]
          },
          "note": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          },
          "next_due": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "description": "Explicit authenticated intervention. Omit next_due to preserve; null removes it. Date is server validated with explicit offset. Replay the same request_id and exact payload on the same asset. Exact replay returns original receipt without writing; changed payload or old revision conflicts. No scan/GET can record maintenance. History is an operator report, not a regulatory certification."
      },
      "AssetCheckReceipt": {
        "type": "object",
        "required": [
          "id",
          "revision",
          "recorded_at",
          "replayed"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "recorded_at": {
            "type": "string",
            "format": "date-time"
          },
          "replayed": {
            "type": "boolean"
          }
        }
      },
      "AssetCheck": {
        "type": "object",
        "required": [
          "id",
          "kind",
          "note",
          "recorded_at",
          "recorded_by",
          "actor_name",
          "asset_revision",
          "page_version",
          "reference"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "inspection",
              "maintenance",
              "issue",
              "configuration"
            ]
          },
          "note": {
            "type": "string",
            "maxLength": 2000
          },
          "recorded_at": {
            "type": "string",
            "format": "date-time"
          },
          "recorded_by": {
            "type": [
              "integer",
              "null"
            ]
          },
          "actor_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255
          },
          "asset_revision": {
            "type": "string",
            "format": "uuid"
          },
          "page_version": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80
          }
        },
        "description": "Immutable internal history. Captured operator name and reference remain historical, including ownership transfer. Numeric database ordering and request hashes are private."
      },
      "Asset": {
        "type": "object",
        "required": [
          "code_id",
          "code_label",
          "code_revision",
          "location_id",
          "campaign_id",
          "asset_id",
          "revision",
          "kind",
          "enabled",
          "details",
          "next_due",
          "timezone",
          "public_reference",
          "public_due",
          "state",
          "page_id",
          "draft_revision",
          "page_version",
          "url",
          "history",
          "history_meta",
          "max_checks"
        ],
        "properties": {
          "code_id": {
            "type": "integer"
          },
          "code_label": {
            "type": "string",
            "maxLength": 160
          },
          "code_revision": {
            "type": "string",
            "maxLength": 64
          },
          "location_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "asset_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "equipment",
              "product"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "details": {
            "type": "object",
            "required": [
              "reference",
              "sku",
              "serial",
              "lot",
              "manufacturer",
              "shelf"
            ],
            "properties": {
              "reference": {
                "type": "string",
                "maxLength": 80
              },
              "sku": {
                "type": "string",
                "maxLength": 80
              },
              "serial": {
                "type": "string",
                "maxLength": 80
              },
              "lot": {
                "type": "string",
                "maxLength": 80
              },
              "manufacturer": {
                "type": "string",
                "maxLength": 120
              },
              "shelf": {
                "type": "string",
                "maxLength": 80
              }
            }
          },
          "next_due": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "timezone": {
            "type": "string",
            "maxLength": 64
          },
          "public_reference": {
            "type": "boolean"
          },
          "public_due": {
            "type": "boolean"
          },
          "state": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "overdue",
              "retired"
            ]
          },
          "page_id": {
            "type": "string",
            "format": "uuid"
          },
          "draft_revision": {
            "type": "string",
            "format": "uuid"
          },
          "page_version": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "history": {
            "type": "array",
            "maxItems": 0
          },
          "history_meta": {
            "type": "null"
          },
          "max_checks": {
            "type": "integer",
            "const": 5000
          }
        },
        "description": "Private metadata accessible with assets:read. History is always empty and history_meta null on metadata/list routes; assets:history is required separately for internal reports. State describes the inventory object, not availability of its public link. A retired asset keeps history. One existing dynamic product QR uses one ordinary QR slot, not a second slot. No merchant stock, physical NFC write or supplier e-ink compatibility is implied."
      },
      "CommerceValues": {
        "type": "object",
        "required": [
          "price_minor",
          "currency",
          "tax_mode",
          "stock_quantity"
        ],
        "properties": {
          "price_minor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 999999999
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "EUR",
              "GBP",
              "CHF",
              "PLN",
              "RON",
              "SEK",
              "NOK",
              "DKK",
              "CZK",
              "HUF",
              "RSD",
              "TRY",
              "RUB",
              "UAH",
              "USD",
              "ISK",
              "ALL",
              "BAM",
              "MKD",
              "MDL",
              "GEL",
              "AMD",
              "AZN",
              null
            ]
          },
          "tax_mode": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "included",
              "excluded",
              "not_applicable",
              null
            ]
          },
          "stock_quantity": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 10000000
          }
        },
        "description": "Complete snapshot, no invented defaults. Null is unknown, zero is explicitly supplied. A non-null price requires currency and tax_mode; null price requires null currency/tax_mode. Supported ISO 4217 minor units: ISK 0; other listed currencies 2. No conversion, tax calculation, sale confirmation or stock reservation."
      },
      "CommerceConfiguration": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "asset_revision",
          "enabled",
          "source_key_id",
          "ttl_minutes",
          "public_price",
          "public_stock",
          "public_quantity"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "asset_revision": {
            "type": "string",
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "source_key_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "ttl_minutes": {
            "type": "integer",
            "enum": [
              5,
              15,
              30,
              60,
              120,
              360,
              720,
              1440
            ]
          },
          "public_price": {
            "type": "boolean"
          },
          "public_stock": {
            "type": "boolean"
          },
          "public_quantity": {
            "type": "boolean"
          }
        },
        "description": "Owner/admin, current 2FA, actual editable-QR/API entitlement. Configured source key must belong to workspace payer, have commerce:write and current expiry/role/2FA/location access. Source-bound key may not manage configuration. Product active and nonempty exact SKU required for enabling; disabling an archived object remains allowed. Changed settings create new policy revision and clear observations/sequence. Unchanged settings are a no-op and preserve live data. Quantity visibility requires public_stock."
      },
      "CommerceObservation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "request_id",
          "sequence",
          "sku",
          "price_minor",
          "currency",
          "tax_mode",
          "stock_quantity",
          "observed_at"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "sequence": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2147483647
          },
          "sku": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "price_minor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 999999999
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "EUR",
              "GBP",
              "CHF",
              "PLN",
              "RON",
              "SEK",
              "NOK",
              "DKK",
              "CZK",
              "HUF",
              "RSD",
              "TRY",
              "RUB",
              "UAH",
              "USD",
              "ISK",
              "ALL",
              "BAM",
              "MKD",
              "MDL",
              "GEL",
              "AMD",
              "AZN",
              null
            ]
          },
          "tax_mode": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "included",
              "excluded",
              "not_applicable",
              null
            ]
          },
          "stock_quantity": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 10000000
          },
          "observed_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "description": "Authenticated configured owner source only; explicit commerce:write. Strict JSON integers, full snapshot. Current policy revision and exact SKU required. Sequence strictly increases, observed_at never goes backward, timestamp within last 7 days and at most 5 minutes ahead. Old versions cannot replace new ones. Stable UUID request_id: exact replay returns original receipt while retained (latest 64 per feed, at most 7 days), changed payload conflicts. After eviction the old sequence is still refused. No write by opening a link. Technical body bound 8192 bytes."
      },
      "CommerceBatchItem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "code_id",
          "revision",
          "request_id",
          "sequence",
          "sku",
          "price_minor",
          "currency",
          "tax_mode",
          "stock_quantity",
          "observed_at"
        ],
        "properties": {
          "code_id": {
            "type": "integer",
            "minimum": 1
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "sequence": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2147483647
          },
          "sku": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "price_minor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 999999999
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "EUR",
              "GBP",
              "CHF",
              "PLN",
              "RON",
              "SEK",
              "NOK",
              "DKK",
              "CZK",
              "HUF",
              "RSD",
              "TRY",
              "RUB",
              "UAH",
              "USD",
              "ISK",
              "ALL",
              "BAM",
              "MKD",
              "MDL",
              "GEL",
              "AMD",
              "AZN",
              null
            ]
          },
          "tax_mode": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "included",
              "excluded",
              "not_applicable",
              null
            ]
          },
          "stock_quantity": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 10000000
          },
          "observed_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CommerceBatch": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/CommerceBatchItem"
            }
          }
        },
        "description": "At most 50 distinct existing products, one transaction, sorted row locks, input-order receipts. Any conflict rolls back all new updates/receipts. Individual stable request IDs permit exact replays; no partial automatic retry. Body bound 65536 bytes. Same 20/minute payer budget as single updates; multiple source keys share it. Does not create QR codes or commercial rights."
      },
      "CommerceReceipt": {
        "type": "object",
        "required": [
          "code_id",
          "id",
          "revision",
          "data_revision",
          "sequence",
          "received_at",
          "replayed"
        ],
        "properties": {
          "code_id": {
            "type": "integer"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "data_revision": {
            "type": "string",
            "format": "uuid"
          },
          "sequence": {
            "type": "integer"
          },
          "received_at": {
            "type": "string",
            "format": "date-time"
          },
          "replayed": {
            "type": "boolean"
          }
        }
      },
      "CommerceFeed": {
        "type": "object",
        "required": [
          "code_id",
          "code_label",
          "code_revision",
          "asset_revision",
          "page_version",
          "location_id",
          "kind",
          "sku",
          "timezone",
          "revision",
          "data_revision",
          "source_key_id",
          "source_name",
          "source_connected",
          "enabled",
          "public_price",
          "public_stock",
          "public_quantity",
          "ttl_minutes",
          "status",
          "source_sequence",
          "values",
          "observed_at",
          "received_at",
          "expires_at"
        ],
        "properties": {
          "code_id": {
            "type": "integer"
          },
          "code_label": {
            "type": "string"
          },
          "code_revision": {
            "type": "string"
          },
          "asset_revision": {
            "type": "string",
            "format": "uuid"
          },
          "page_version": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "location_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "product",
              "equipment"
            ]
          },
          "sku": {
            "type": "string"
          },
          "timezone": {
            "type": "string"
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "data_revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "source_key_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "source_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "source_connected": {
            "type": "boolean"
          },
          "enabled": {
            "type": "boolean"
          },
          "public_price": {
            "type": "boolean"
          },
          "public_stock": {
            "type": "boolean"
          },
          "public_quantity": {
            "type": "boolean"
          },
          "ttl_minutes": {
            "type": "integer",
            "enum": [
              5,
              15,
              30,
              60,
              120,
              360,
              720,
              1440
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "disabled",
              "archived",
              "reconnect",
              "disconnected",
              "awaiting",
              "stale",
              "live"
            ]
          },
          "source_sequence": {
            "type": "integer"
          },
          "values": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CommerceValues"
              },
              {
                "type": "null"
              }
            ]
          },
          "observed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "received_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "description": "Private metadata, separate commerce:read permission. Last received values may be stale; status/expiry must be checked. Expiry is min(observed_at, received_at)+ttl. Receipt/key/clock/site/SKU/policy are rechecked before delivery. This data is independent from material intervention history. No external merchant connector or physical e-ink/NFC compatibility is implied."
      },
      "MonitoringConfiguration": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "enabled",
          "interval_minutes"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "interval_minutes": {
            "type": "integer",
            "enum": [
              30,
              60,
              180,
              360,
              1440
            ]
          }
        },
        "description": "Owner/admin, current 2FA, actual editable-QR entitlement when enabling. Editable external URL QR only. Current policy revision required, null initially. Unchanged configuration is a no-op. Disabling resets claims and closes incidents without claiming recovery. All targets come from owned QR/routing, never from request URLs."
      },
      "MonitoringAlertPreference": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "enabled"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          }
        },
        "description": "Personal opt-in for current authenticated actor and owned QR. Verified current account email only, never an arbitrary recipient. Current preference revision, null initially. At most 100 opted-in users per QR."
      },
      "MonitoringAcknowledge": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        },
        "description": "Editor or manager. Explicit acknowledgement suppresses pending initial alert, does not repair/close the incident. Current destination epoch and incident revision required."
      },
      "MonitoringTarget": {
        "type": "object",
        "required": [
          "key",
          "type",
          "status",
          "host",
          "checked_at",
          "incident"
        ],
        "properties": {
          "id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "key": {
            "type": "string",
            "maxLength": 60
          },
          "type": {
            "type": "string",
            "enum": [
              "primary",
              "fallback",
              "rule",
              "variant_a",
              "variant_b"
            ]
          },
          "label": {
            "type": "string"
          },
          "host": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "paused",
              "changed",
              "operator_pending",
              "checking",
              "incident",
              "healthy",
              "failure",
              "redirect",
              "restricted",
              "unknown",
              "stale",
              "unsupported"
            ]
          },
          "observation": {
            "type": [
              "string",
              "null"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "http_status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "latency_ms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 5000
          },
          "failures": {
            "type": "integer",
            "minimum": 0,
            "maximum": 3
          },
          "checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "next_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "incident": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "revision": {
                "type": "string",
                "format": "uuid"
              },
              "opened_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "acknowledged_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          }
        },
        "description": "Domain and technical outcome only, no full URL/path/query/auth token/IP/body. HEAD 2xx is a point-in-time observation; 3xx is not followed and never means the final page was verified. 4xx restrictions are inconclusive, 404/410/5xx or network/TLS/DNS failures contribute to confirmation. Three failures to open, two healthy checks to recover. Old observations are stale; eligibility loss pauses presentation. Best-effort cadence, no uptime SLA."
      },
      "MonitoringData": {
        "type": "object",
        "required": [
          "code_id",
          "code_label",
          "revision",
          "enabled",
          "interval_minutes",
          "targets",
          "history",
          "alerts"
        ],
        "properties": {
          "code_id": {
            "type": "integer"
          },
          "code_label": {
            "type": "string"
          },
          "location_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "interval_minutes": {
            "type": "integer"
          },
          "manageable": {
            "type": "boolean"
          },
          "editable": {
            "type": "boolean"
          },
          "controllable": {
            "type": "boolean"
          },
          "eligible": {
            "type": "boolean"
          },
          "operator_ready": {
            "type": "boolean"
          },
          "targets": {
            "type": "array",
            "maxItems": 15,
            "items": {
              "$ref": "#/components/schemas/MonitoringTarget"
            }
          },
          "history": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "host": {
                  "type": "string"
                },
                "opened_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "closed_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "closed_reason": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "acknowledged_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "alerts": {
            "type": "object",
            "properties": {
              "revision": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uuid"
              },
              "enabled": {
                "type": "boolean"
              },
              "mail_ready": {
                "type": "boolean"
              },
              "messages": {
                "type": "array",
                "maxItems": 20,
                "items": {
                  "type": "object",
                  "properties": {
                    "kind": {
                      "type": "string",
                      "enum": [
                        "failure",
                        "recovered"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "sending",
                        "sent",
                        "blocked",
                        "uncertain",
                        "limited"
                      ]
                    },
                    "created_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "sent_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        },
        "description": "Own workspace/location only, row snapshots and native list totals rechecked before delivery. Message diagnostics are personal, never another subscriber email. No commercial rights derived from input. Background network gate and dedicated cron worker need deployment verification. No scan, conversion or redirect change is created by these checks."
      },
      "PagePublicationPrepare": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "request_id"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "pages": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "uniqueItems": true,
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "campaign_id": {
            "type": "integer",
            "minimum": 1
          },
          "batch_uuid": {
            "type": "string",
            "format": "uuid"
          }
        },
        "oneOf": [
          {
            "required": [
              "pages"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "campaign_id"
                  ]
                },
                {
                  "required": [
                    "batch_uuid"
                  ]
                }
              ]
            }
          },
          {
            "required": [
              "campaign_id"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "pages"
                  ]
                },
                {
                  "required": [
                    "batch_uuid"
                  ]
                }
              ]
            }
          },
          {
            "required": [
              "batch_uuid"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "pages"
                  ]
                },
                {
                  "required": [
                    "campaign_id"
                  ]
                }
              ]
            }
          }
        ]
      },
      "PagePublicationDecision": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "action",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "enum": [
              "publish",
              "cancel"
            ]
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "PagePublicationItem": {
        "type": "object",
        "required": [
          "page_id",
          "qr_code_id",
          "published_version_id"
        ],
        "properties": {
          "page_id": {
            "type": "string",
            "format": "uuid"
          },
          "qr_code_id": {
            "type": "integer"
          },
          "label": {
            "type": "string",
            "description": "Only returned to read-authorized responses; omitted for write-only decisions."
          },
          "type": {
            "type": "string"
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "published_version_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "PagePublication": {
        "type": "object",
        "required": [
          "id",
          "revision",
          "status",
          "count",
          "expires_at",
          "items",
          "conflicting_page_ids"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "preview",
              "published",
              "cancelled"
            ]
          },
          "count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "applied_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "conflicting_page_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PagePublicationItem"
            }
          }
        }
      },
      "PageImportRow": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "label",
          "title",
          "intro"
        ],
        "properties": {
          "label": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "intro": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 800
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 128
          },
          "blocks": {
            "type": "array",
            "maxItems": 40,
            "description": "Safe text/link/menu_item/event/faq/callout/form/external video/audio sections; no file references. JSON serialized input <=16KiB per row, native CSV/XLSX cell<=4096characters. Empty intro allowed only with nonempty valid sections.",
            "items": {
              "type": "object"
            }
          },
          "media_source_page_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Own source page for already normalized files referenced by sections. Per-row choice overrides the common option. Files copied after confirmation only; total copy footprint <=8 MiB."
          }
        },
        "description": "Text and structured safe sections, one language per import. No media references, HTML interpretation, auto translation, interactive catalogue or implicit publication. Atomic confirm revalidates canonical snapshot and absence of file references. References unique within the import."
      },
      "PageImportPrepare": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "request_id",
          "page_type",
          "locale"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "page_type": {
            "type": "string",
            "enum": [
              "links",
              "menu",
              "guide",
              "product",
              "event",
              "app",
              "article",
              "pass"
            ]
          },
          "locale": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "brand_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "rows": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/PageImportRow"
            }
          },
          "file": {
            "type": "string",
            "format": "binary",
            "description": "CSV/XLSX <=1 MiB; headers label,title,intro, optional reference. Formula cells rejected; no network/evaluation."
          },
          "media_source_page_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Common own source page for normalized files. Row-level media_source_page_id overrides this value."
          }
        },
        "oneOf": [
          {
            "required": [
              "rows"
            ],
            "not": {
              "required": [
                "file"
              ]
            }
          },
          {
            "required": [
              "file"
            ],
            "not": {
              "required": [
                "rows"
              ]
            }
          }
        ]
      },
      "PageImportConfirmation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "minLength": 64,
            "maxLength": 64
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "PageImport": {
        "type": "object",
        "required": [
          "id",
          "status",
          "revision",
          "count",
          "expires_at",
          "completed_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "review",
              "completed"
            ]
          },
          "revision": {
            "type": "string",
            "minLength": 64,
            "maxLength": 64
          },
          "count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "rows": {
            "type": "array",
            "description": "Only returned by the read endpoint with pages:read + batches:read. Snapshot at import time, not current drafts.",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "intro": {
                  "type": "string"
                },
                "locale": {
                  "type": "string"
                },
                "page_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uuid"
                },
                "qr_code_id": {
                  "type": [
                    "integer",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "AnalyticsPdfReport": {
        "type": "object",
        "required": [
          "id",
          "revision",
          "status",
          "period",
          "language",
          "expires_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "rendering",
              "ready",
              "failed"
            ]
          },
          "period": {
            "type": "object",
            "required": [
              "from",
              "to",
              "timezone"
            ],
            "properties": {
              "from": {
                "type": "string",
                "format": "date"
              },
              "to": {
                "type": "string",
                "format": "date"
              },
              "timezone": {
                "type": "string"
              }
            }
          },
          "language": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "generated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "pdf_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "maximum": 2097152
          }
        }
      },
      "CreateAnalyticsPdfReport": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "request_id",
          "timezone",
          "language",
          "include_codes"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "from": {
            "type": "string",
            "format": "date"
          },
          "to": {
            "type": "string",
            "format": "date"
          },
          "timezone": {
            "type": "string"
          },
          "language": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "include_codes": {
            "type": "boolean"
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "code_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          }
        }
      },
      "ConfigureReportSchedule": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "enabled",
          "frequency",
          "timezone",
          "language",
          "include_codes"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "frequency": {
            "type": "string",
            "enum": [
              "weekly",
              "monthly"
            ]
          },
          "timezone": {
            "type": "string"
          },
          "language": {
            "type": "string",
            "enum": [
              "fr",
              "en",
              "it",
              "es",
              "de",
              "pt",
              "ro",
              "ru",
              "uk",
              "nl",
              "pl",
              "cs",
              "sk",
              "hu",
              "el",
              "bg",
              "hr",
              "sr",
              "tr",
              "sv",
              "da",
              "fi",
              "nb"
            ]
          },
          "include_codes": {
            "type": "boolean"
          },
          "campaign_id": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          }
        }
      },
      "ReportEmailRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "request_id",
          "revision",
          "confirmed"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "ReportEmailReceipt": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "sending",
              "sent",
              "blocked",
              "uncertain"
            ]
          }
        }
      },
      "ReportSchedule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "frequency": {
            "type": "string",
            "enum": [
              "weekly",
              "monthly"
            ]
          },
          "next_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_status": {
            "type": [
              "string",
              "null"
            ]
          },
          "configuration": {
            "type": "object",
            "properties": {
              "timezone": {
                "type": "string"
              },
              "language": {
                "type": "string",
                "enum": [
                  "fr",
                  "en",
                  "it",
                  "es",
                  "de",
                  "pt",
                  "ro",
                  "ru",
                  "uk",
                  "nl",
                  "pl",
                  "cs",
                  "sk",
                  "hu",
                  "el",
                  "bg",
                  "hr",
                  "sr",
                  "tr",
                  "sv",
                  "da",
                  "fi",
                  "nb"
                ]
              },
              "include_codes": {
                "type": "boolean"
              },
              "campaign_id": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 1
              }
            }
          }
        }
      },
      "GalleryItem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "asset_id",
          "alt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "asset_id": {
            "type": "string",
            "format": "uuid"
          },
          "alt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 240
          },
          "caption": {
            "type": "string",
            "maxLength": 240
          }
        }
      },
      "FaqItem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "question",
          "answer"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "question": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "answer": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          }
        }
      },
      "PortabilityExport": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "history",
          "confirmed"
        ],
        "properties": {
          "codes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 25,
            "uniqueItems": true,
            "items": {
              "type": "integer",
              "minimum": 1
            }
          },
          "campaign_id": {
            "type": "integer",
            "minimum": 1
          },
          "history": {
            "type": "boolean"
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "PortabilityImportReceipt": {
        "type": "object",
        "required": [
          "id",
          "revision",
          "status",
          "expires_at",
          "codes"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "preview",
              "completed"
            ]
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "codes": {
            "type": "array",
            "maxItems": 25,
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "content_type": {
                  "type": "string"
                },
                "restorable": {
                  "type": "boolean"
                }
              }
            }
          },
          "result": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "object",
              "properties": {
                "source_id": {
                  "type": "integer"
                },
                "id": {
                  "type": "integer"
                },
                "short_url": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uri"
                }
              }
            }
          }
        }
      },
      "PortabilityConfirm": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "PortabilityManifest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "format",
          "version",
          "codes",
          "campaigns",
          "media"
        ],
        "properties": {
          "format": {
            "type": "string",
            "const": "qrissimo-portability"
          },
          "version": {
            "type": "integer",
            "const": 1
          },
          "codes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 25,
            "items": {
              "type": "object",
              "description": "Owned current kind/content_type/content/style/campaign_id, schedule state, original_short_url, optional page draft/published/versions and optional redacted QR history. No Wi-Fi password or raw payload. Restore creates new IDs, slugs and immutable media; it does not restore active historical versions, locations, domains, roles, payments, policies or automation."
            }
          },
          "campaigns": {
            "type": "array",
            "maxItems": 25,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                }
              }
            }
          },
          "media": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "description": "Referenced normalized PNG/JPEG/PDF: id,page_id,name,type,mime,bytes,path,sha256. Entries live at media/{page_uuid}/{asset_uuid}.{png|jpg|pdf}. Normalized again outside SQL before restoring."
            }
          }
        }
      },
      "PrintGeometry": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "orientation": {
            "type": "string",
            "enum": [
              "portrait",
              "landscape"
            ],
            "default": "portrait"
          },
          "width": {
            "type": "number",
            "minimum": 20,
            "maximum": 277
          },
          "height": {
            "type": "number",
            "minimum": 20,
            "maximum": 277
          },
          "margin_x": {
            "type": "number",
            "minimum": 5,
            "maximum": 50
          },
          "margin_y": {
            "type": "number",
            "minimum": 5,
            "maximum": 50
          },
          "gap_x": {
            "type": "number",
            "minimum": 0,
            "maximum": 20
          },
          "gap_y": {
            "type": "number",
            "minimum": 0,
            "maximum": 20
          },
          "copies": {
            "type": "integer",
            "minimum": 1,
            "maximum": 48
          },
          "skip": {
            "type": "integer",
            "minimum": 0,
            "maximum": 47
          },
          "crop_marks": {
            "type": "boolean",
            "default": false
          },
          "heading_template": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 160,
            "description": "Literal text with {{label}}, {{id}}, {{url}}, {{index}}, {{total}}. Index and total count distinct QR codes; no expressions or private payloads."
          },
          "detail_template": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 160,
            "description": "Optional one-line detail below the composition, using the same bounded variables."
          }
        },
        "description": "A4 physical millimetres. Custom layout requires width and height. At most 48 cells per page, 288 labels and 24 pages. skip affects the first page only and must be below capacity. Crop marks require gaps >=5mm where neighbouring cells exist. Legacy layouts allow only copies, skip and crop_marks (their zero gaps usually prevent crop marks)."
      },
      "BrandIdentity": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "display_name": {
            "type": "string",
            "maxLength": 60
          },
          "signature": {
            "type": "string",
            "maxLength": 100
          },
          "font": {
            "type": "string",
            "enum": [
              "sans",
              "serif",
              "mono"
            ]
          },
          "logo": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "mime": {
                "type": "string",
                "enum": [
                  "image/png",
                  "image/jpeg"
                ]
              },
              "data": {
                "type": "string",
                "contentEncoding": "base64",
                "description": "Server-normalized private raster, no metadata or SVG."
              },
              "width": {
                "type": "integer",
                "minimum": 1,
                "maximum": 1600
              },
              "height": {
                "type": "integer",
                "minimum": 1,
                "maximum": 1600
              }
            },
            "required": [
              "mime",
              "data",
              "width",
              "height"
            ]
          }
        },
        "additionalProperties": false
      },
      "BrandIdentityUpdate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "display_name",
          "font",
          "signature"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "display_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "font": {
            "type": "string",
            "enum": [
              "sans",
              "serif",
              "mono"
            ]
          },
          "signature": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100
          },
          "remove_logo": {
            "type": "boolean",
            "default": false
          },
          "logo_base64": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 350000,
            "description": "Optional raw base64 PNG/JPEG, <=256KiB decoded. Not a data URL or remote URL. Mutually exclusive with remove_logo:true. Stored normalized output <=256KiB. Omit to retain current logo."
          }
        }
      },
      "NfcRecord": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "recordType",
          "data"
        ],
        "properties": {
          "recordType": {
            "type": "string",
            "enum": [
              "url"
            ]
          },
          "data": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "NfcSnapshot": {
        "type": "object",
        "required": [
          "code_id",
          "url",
          "revision",
          "records",
          "ndef_bytes",
          "sha256"
        ],
        "properties": {
          "code_id": {
            "type": "integer"
          },
          "label": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "revision": {
            "type": "string",
            "minLength": 64,
            "maxLength": 64
          },
          "records": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1,
            "items": {
              "$ref": "#/components/schemas/NfcRecord"
            }
          },
          "ndef_bytes": {
            "type": "integer"
          },
          "sha256": {
            "type": "string",
            "minLength": 64,
            "maxLength": 64
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "IndustrialSymbolRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "datamatrix",
              "gs1_datamatrix",
              "digital_link"
            ]
          },
          "text": {
            "type": "string",
            "maxLength": 256
          },
          "gtin": {
            "type": "string",
            "pattern": "^[0-9]{14}$"
          },
          "lot": {
            "type": "string",
            "maxLength": 20
          },
          "serial": {
            "type": "string",
            "maxLength": 20
          },
          "expiry": {
            "type": "string",
            "format": "date"
          },
          "base_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 500
          },
          "size": {
            "type": "integer",
            "minimum": 128,
            "maximum": 2048,
            "default": 512
          },
          "format": {
            "type": "string",
            "enum": [
              "json",
              "svg",
              "png"
            ],
            "default": "json"
          }
        }
      },
      "WebhookCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "request_id",
          "name",
          "endpoint",
          "topics",
          "confirmed"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "endpoint": {
            "type": "string",
            "format": "uri",
            "maxLength": 1000,
            "description": "Public HTTPS443, no IP/credentials/fragment/redirect; pinned public DNS."
          },
          "topics": {
            "type": "array",
            "minItems": 1,
            "maxItems": 7,
            "uniqueItems": true,
            "items": {
              "type": "string",
              "enum": [
                "qr.changed",
                "feedback.created",
                "restaurant.requested",
                "journey.requested",
                "stock.updated",
                "conversion.confirmed",
                "pass.redeemed"
              ]
            }
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "WebhookRevision": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "WebhookDisable": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          },
          "rotate": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "WebhookConfirm": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "confirmed"
        ],
        "properties": {
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "WebhookProfile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "endpoint": {
            "type": "string"
          },
          "topics": {
            "type": "array",
            "minItems": 1,
            "maxItems": 7,
            "uniqueItems": true,
            "items": {
              "type": "string",
              "enum": [
                "qr.changed",
                "feedback.created",
                "restaurant.requested",
                "journey.requested",
                "stock.updated",
                "conversion.confirmed",
                "pass.redeemed"
              ]
            }
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "state": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "disabled"
            ]
          },
          "secret": {
            "type": "string",
            "description": "Only first create or explicit rotation; never list or idempotent replay."
          },
          "location_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "WebhookMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "client_webhook_id": {
            "type": "string",
            "format": "uuid"
          },
          "topic": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "sending",
              "delivered",
              "failed",
              "blocked",
              "uncertain"
            ]
          },
          "http_status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "required": [
          "event_id",
          "topic",
          "occurred_at",
          "data"
        ],
        "properties": {
          "event_id": {
            "type": "string",
            "format": "uuid"
          },
          "topic": {
            "type": "string"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "description": "Only IDs and states, never contact or payment details. webhook.test includes nonce for HMAC proof."
          }
        }
      },
      "FeedbackFile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "mime": {
            "type": "string",
            "enum": [
              "image/png",
              "image/jpeg",
              "application/pdf"
            ]
          },
          "bytes": {
            "type": "integer",
            "maximum": 2097152
          },
          "origin": {
            "type": "string",
            "enum": [
              "visitor",
              "reply"
            ]
          },
          "downloads": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "NfcBatchPreview": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "codes"
        ],
        "properties": {
          "codes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "uniqueItems": true,
            "items": {
              "type": "integer",
              "minimum": 1
            }
          }
        }
      },
      "NfcBatchExport": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "codes",
          "revision",
          "format",
          "confirmed"
        ],
        "properties": {
          "codes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "uniqueItems": true,
            "items": {
              "type": "integer",
              "minimum": 1
            }
          },
          "revision": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$"
          },
          "format": {
            "type": "string",
            "enum": [
              "json",
              "csv",
              "zip"
            ]
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "NfcBatchManifest": {
        "type": "object",
        "properties": {
          "version": {
            "type": "integer",
            "const": 1
          },
          "revision": {
            "type": "string"
          },
          "codes": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/NfcSnapshot"
            }
          }
        }
      },
      "BrandRule": {
        "type": "object",
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "source_kit_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "source_revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "locked_fields": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "modules",
                "finders",
                "foreground",
                "background",
                "template",
                "caption",
                "arrow",
                "caption_font",
                "logo_data",
                "caption_position",
                "caption_offset",
                "caption_scale",
                "caption_align",
                "composition_mode",
                "logo_mode",
                "composition_layers",
                "caption_rotation",
                "caption_dx",
                "caption_dy",
                "arrow_dx",
                "arrow_dy",
                "arrow_scale",
                "logo_dx",
                "logo_dy",
                "logo_scale",
                "caption_color",
                "arrow_color",
                "color_mode",
                "gradient_color",
                "gradient_angle"
              ]
            },
            "maxItems": 30
          },
          "values": {
            "type": "object",
            "description": "Canonical fixed values copied from an owned active kit; encrypted at rest."
          }
        }
      },
      "BrandRuleUpdate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "enabled",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "kit_id": {
            "type": "integer",
            "minimum": 1
          },
          "kit_revision": {
            "type": "string",
            "format": "uuid"
          },
          "locked_fields": {
            "type": "array",
            "minItems": 1,
            "maxItems": 30,
            "uniqueItems": true,
            "items": {
              "type": "string",
              "enum": [
                "modules",
                "finders",
                "foreground",
                "background",
                "template",
                "caption",
                "arrow",
                "caption_font",
                "logo_data",
                "caption_position",
                "caption_offset",
                "caption_scale",
                "caption_align",
                "composition_mode",
                "logo_mode",
                "composition_layers",
                "caption_rotation",
                "caption_dx",
                "caption_dy",
                "arrow_dx",
                "arrow_dy",
                "arrow_scale",
                "logo_dx",
                "logo_dy",
                "logo_scale",
                "caption_color",
                "arrow_color",
                "color_mode",
                "gradient_color",
                "gradient_angle"
              ]
            }
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        },
        "description": "kit_id/kit_revision and locked_fields required when enabled. Snapshot values chosen by server, not request. Requires manage role, current2FA/Business entitlement and global view."
      },
      "PageTemplate": {
        "type": "object",
        "required": [
          "page_type",
          "revision",
          "enabled",
          "locked_fields",
          "locked_blocks",
          "snapshot"
        ],
        "properties": {
          "page_type": {
            "type": "string",
            "enum": [
              "links",
              "menu",
              "guide",
              "product",
              "event",
              "app",
              "article",
              "pass"
            ]
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "source_page_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "source_revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "locked_fields": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "brand.name",
                "brand.accent",
                "brand.font",
                "brand.signature",
                "title",
                "intro",
                "color_mode",
                "gradient_color",
                "gradient_angle"
              ]
            },
            "maxItems": 30,
            "uniqueItems": true
          },
          "locked_blocks": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "maxItems": 40,
            "uniqueItems": true
          },
          "snapshot": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/HostedPageSnapshot"
              },
              {
                "type": "array",
                "maxItems": 0
              }
            ],
            "description": "Immutable editorial snapshot; empty array when disabled. No private source files, logos or booking catalogs."
          }
        }
      },
      "PageTemplateUpdate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "enabled",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "source_page_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "source_revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "locked_fields": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "brand.name",
                "brand.accent",
                "brand.font",
                "brand.signature",
                "title",
                "intro",
                "color_mode",
                "gradient_color",
                "gradient_angle"
              ]
            },
            "maxItems": 30,
            "uniqueItems": true
          },
          "locked_blocks": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "maxItems": 40,
            "uniqueItems": true
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        },
        "description": "When enabled: source_page_id/source_revision required, at least one locked field or block. Source must be an accessible draft of the same type, ready to publish. Source text is explicitly shared with editors across locations. Global owner/admin, 2FA, current Business entitlement; pages:read required as well as page_templates:write. MAX request 8 KiB."
      },
      "PageTemplateApply": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "revision",
          "template_revision",
          "confirmed"
        ],
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "template_revision": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed": {
            "type": "boolean",
            "const": true
          }
        },
        "description": "Explicit atomic draft adoption in all central template languages. Requires page_templates:apply and pages:write. Does not publish. Editable contents and existing private files preserved; too many sections rejected without truncation."
      },
      "MailSenderInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "address": {
            "type": "string",
            "format": "email",
            "maxLength": 254
          },
          "revision": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "confirmed": {
            "const": true
          }
        },
        "required": [
          "name",
          "address",
          "revision",
          "confirmed"
        ]
      },
      "MailRevisionInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed": {
            "const": true
          }
        },
        "required": [
          "revision",
          "confirmed"
        ]
      },
      "MailSenderStateInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "confirmed": {
            "const": true
          },
          "enabled": {
            "type": "boolean"
          }
        },
        "required": [
          "revision",
          "confirmed",
          "enabled"
        ]
      },
      "MailSender": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "revision",
          "name",
          "address",
          "domain",
          "enabled",
          "dns_verified",
          "verified_at",
          "checked_at",
          "checking",
          "relay_ready",
          "records"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 80
          },
          "address": {
            "type": "string",
            "format": "email"
          },
          "domain": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "dns_verified": {
            "type": "boolean"
          },
          "verified_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "checking": {
            "type": "boolean"
          },
          "relay_ready": {
            "type": "boolean"
          },
          "records": {
            "type": "array",
            "minItems": 2,
            "maxItems": 2,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "type",
                "name",
                "value"
              ],
              "properties": {
                "type": {
                  "const": "TXT"
                },
                "name": {
                  "type": "string"
                },
                "value": {
                  "type": "string",
                  "description": "Public DNS value, never the private signing key"
                }
              }
            }
          }
        }
      },
      "OutboundMail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "kind",
          "state",
          "created_at",
          "event_at",
          "status_code",
          "recipient"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "feedback",
              "invitation",
              "document",
              "report",
              "monitor",
              "account"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "prepared",
              "accepted",
              "delivered",
              "deferred",
              "bounced",
              "complaint",
              "uncertain",
              "blocked"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "event_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "status_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "recipient": {
            "type": [
              "string",
              "null"
            ],
            "description": "Masked local part; no full address, hash, body or token"
          }
        }
      },
      "MailSuppression": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "revision",
          "recipient",
          "reason",
          "channel"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "revision": {
            "type": "string",
            "format": "uuid"
          },
          "recipient": {
            "type": [
              "string",
              "null"
            ]
          },
          "reason": {
            "enum": [
              "bounced",
              "complaint"
            ]
          },
          "channel": {
            "enum": [
              "client",
              "system"
            ]
          }
        }
      },
      "MailMessagePage": {
        "type": "object",
        "required": [
          "data",
          "current_page",
          "per_page",
          "next_page_url",
          "prev_page_url"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutboundMail"
            }
          },
          "current_page": {
            "type": "integer"
          },
          "per_page": {
            "type": "integer"
          },
          "next_page_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "prev_page_url": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "MailSuppressionPage": {
        "type": "object",
        "required": [
          "data",
          "current_page",
          "per_page",
          "next_page_url",
          "prev_page_url"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailSuppression"
            }
          },
          "current_page": {
            "type": "integer"
          },
          "per_page": {
            "type": "integer"
          },
          "next_page_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "prev_page_url": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "MailJournal": {
        "type": "object",
        "required": [
          "sender",
          "messages",
          "pauses"
        ],
        "properties": {
          "sender": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/MailSender"
              },
              {
                "type": "null"
              }
            ]
          },
          "messages": {
            "$ref": "#/components/schemas/MailMessagePage"
          },
          "pauses": {
            "$ref": "#/components/schemas/MailSuppressionPage"
          }
        }
      }
    }
  }
}
