{
  "openapi": "3.1.0",
  "info": {
    "title": "Hovercode API",
    "version": "2.0.0",
    "summary": "Create and manage QR codes, PDF codes, short links, landing pages, forms and GS1 Digital Link codes.",
    "description": "Hovercode's REST API (v2). Every request needs an `Authorization: Token <token>` header (find the token and your workspace ID in account settings). Request bodies must be JSON; anything else returns 415. Call it from a back end, never the browser. Requires the Business plan, but can be tested for free. Paths are also served without the trailing slash.\n\n## Rate limits\n\nPer API token, in one-minute windows: 120 reads (GET) and 60 writes per minute; 20 per minute per IP with no or a rejected token. Every response carries `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` and `RateLimit-Policy`; a 429 carries `Retry-After`. See https://hovercode.com/api/#rate-limits.",
    "termsOfService": "https://hovercode.com/terms/",
    "contact": {
      "name": "Hovercode",
      "url": "https://hovercode.com/contact/"
    }
  },
  "externalDocs": {
    "description": "Hovercode API documentation",
    "url": "https://hovercode.com/api/"
  },
  "servers": [
    {
      "url": "https://hovercode.com/api/v2"
    }
  ],
  "security": [
    {
      "TokenAuth": []
    }
  ],
  "tags": [
    {
      "name": "QR codes"
    },
    {
      "name": "Folders"
    },
    {
      "name": "Tags"
    },
    {
      "name": "Analytics"
    },
    {
      "name": "PDF codes"
    },
    {
      "name": "Short links"
    },
    {
      "name": "Landing pages"
    },
    {
      "name": "Forms"
    },
    {
      "name": "GS1 Digital Link"
    }
  ],
  "paths": {
    "/hovercode/create/": {
      "post": {
        "operationId": "createQRCode",
        "tags": [
          "QR codes"
        ],
        "summary": "Create a QR code",
        "description": "Generates a QR code. Returns the code with its SVG inline; set `generate_png: true` to also get `.png` and `.svg` file URLs (slower). `qr_type` defaults to `Link`; use `Text` for plain text, `vCard` with a nested `vcard` object, or `GS1` with a `gs1_product` object (or existing product id). Returns 200, not 201.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QRCodeCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The created QR code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QRCode"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/workspace/{workspace_id}/hovercodes/": {
      "get": {
        "operationId": "listQRCodes",
        "tags": [
          "QR codes"
        ],
        "summary": "List QR codes",
        "description": "The workspace's QR codes, newest first. Paginated 50 per page (max 200). `include=scans` adds `total_scans`/`unique_scans` (lifetime by default; use `days` or `start`/`end` for a window), `include=tags` adds each code's tags.",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkspaceId"
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Search links, display names, shortlink URLs and tags."
          },
          {
            "name": "folder",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Folder id or slug; `none` for unfiled codes."
          },
          {
            "name": "subfolders",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "With `folder`, include codes in its subfolders."
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "style": "form",
            "explode": false,
            "description": "Comma-separated: `scans`, `tags`.",
            "examples": {
              "both": {
                "value": "scans,tags"
              }
            }
          },
          {
            "$ref": "#/components/parameters/Days"
          },
          {
            "$ref": "#/components/parameters/Start"
          },
          {
            "$ref": "#/components/parameters/End"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize200"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Page of QR codes.",
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Pagination"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/QRCodeListItem"
                              }
                            }
                          }
                        }
                      ]
                    },
                    {
                      "$ref": "#/components/schemas/EmptyResults"
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/workspace/{workspace_id}/templates/": {
      "get": {
        "operationId": "listTemplates",
        "tags": [
          "QR codes"
        ],
        "summary": "List templates",
        "description": "Design templates: the workspace's own saved templates first, then Hovercode's built-in designs (`is_default: true`). Paginated 50 per page (max 200).",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkspaceId"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize200"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Page of templates.",
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Pagination"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Template"
                              }
                            }
                          }
                        }
                      ]
                    },
                    {
                      "$ref": "#/components/schemas/EmptyResults"
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/hovercode/{qr_code_id}/": {
      "get": {
        "operationId": "getQRCode",
        "tags": [
          "QR codes"
        ],
        "summary": "Get a QR code",
        "description": "A single QR code, with lifetime `total_scans`/`unique_scans`, tags and folder.",
        "parameters": [
          {
            "$ref": "#/components/parameters/QRCodeId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QRCodeDetail"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/hovercode/{qr_code_id}/activity/": {
      "get": {
        "operationId": "getQRCodeActivity",
        "tags": [
          "Analytics"
        ],
        "summary": "Get QR code tracking activity",
        "description": "Individual scans, newest first. Paginated (default 50, max 200). Optional `start`/`end` window.",
        "parameters": [
          {
            "$ref": "#/components/parameters/QRCodeId"
          },
          {
            "$ref": "#/components/parameters/Start"
          },
          {
            "$ref": "#/components/parameters/End"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize200"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Pagination"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "results": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Activity"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/hovercode/{qr_code_id}/analytics/": {
      "get": {
        "operationId": "getQRCodeAnalytics",
        "tags": [
          "Analytics"
        ],
        "summary": "Get scan analytics",
        "description": "Aggregated stats for one code. Last 30 days by default; `days` (up to 365) or an explicit `start`/`end` range (`start` alone means since then).",
        "parameters": [
          {
            "$ref": "#/components/parameters/QRCodeId"
          },
          {
            "$ref": "#/components/parameters/Days"
          },
          {
            "$ref": "#/components/parameters/Start"
          },
          {
            "$ref": "#/components/parameters/End"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScanAnalytics"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/workspace/{workspace_id}/analytics/": {
      "get": {
        "operationId": "getWorkspaceAnalytics",
        "tags": [
          "Analytics"
        ],
        "summary": "Get workspace analytics",
        "description": "One aggregate for the whole workspace, same shape as code analytics plus `top_codes` ranked within the same window.",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkspaceId"
          },
          {
            "$ref": "#/components/parameters/Days"
          },
          {
            "$ref": "#/components/parameters/Start"
          },
          {
            "$ref": "#/components/parameters/End"
          },
          {
            "name": "top",
            "in": "query",
            "description": "Number of top codes (default 10, max 100).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceAnalytics"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/hovercode/{qr_code_id}/download/": {
      "get": {
        "operationId": "downloadQRCode",
        "tags": [
          "QR codes"
        ],
        "summary": "Download images",
        "description": "Print-ready image URLs: high-resolution `png_url` and vector `svg_url`. Images are generated on first request. Returns 400 for a landing page's code that hasn't been published yet.",
        "parameters": [
          {
            "$ref": "#/components/parameters/QRCodeId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Download"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/hovercode/{qr_code_id}/update/": {
      "post": {
        "operationId": "updateQRCode",
        "tags": [
          "QR codes"
        ],
        "summary": "Update a QR code",
        "description": "Change `display_name`, `qr_data` (dynamic codes only), `gps_tracking`, or `folder` (id or slug; null removes it from its folder). Partial update.",
        "parameters": [
          {
            "$ref": "#/components/parameters/QRCodeId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QRCodeUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated QR code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QRCode"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "patch": {
        "operationId": "patchQRCode",
        "tags": [
          "QR codes"
        ],
        "summary": "Update a QR code (PATCH)",
        "description": "Change `display_name`, `qr_data` (dynamic codes only), `gps_tracking`, or `folder` (id or slug; null removes it from its folder). Partial update.",
        "parameters": [
          {
            "$ref": "#/components/parameters/QRCodeId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QRCodeUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated QR code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QRCode"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/hovercode/{qr_code_id}/design/": {
      "patch": {
        "operationId": "updateQRCodeDesign",
        "tags": [
          "QR codes"
        ],
        "summary": "Update a QR code's design",
        "description": "Restyle an existing code in place; its destination and scan history are unchanged. The QR is re-rendered and the new `svg` returned. For codes created through the API (without a template), eye and frame colors follow `primary_color` unless `eye_color`/`frame_color` are set explicitly.",
        "parameters": [
          {
            "$ref": "#/components/parameters/QRCodeId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QRCodeDesign"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The restyled QR code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QRCode"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/hovercode/{qr_code_id}/delete/": {
      "delete": {
        "operationId": "deleteQRCode",
        "tags": [
          "QR codes"
        ],
        "summary": "Delete a QR code",
        "description": "Deletes the code. Returns 204.",
        "parameters": [
          {
            "$ref": "#/components/parameters/QRCodeId"
          }
        ],
        "responses": {
          "204": {
            "$ref": "#/components/responses/NoContent"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/workspace/{workspace_id}/tags/": {
      "get": {
        "operationId": "listTags",
        "tags": [
          "Tags"
        ],
        "summary": "List tags",
        "description": "The workspace's tags, by title. Paginated 50 per page (max 200).",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkspaceId"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize200"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Page of tags.",
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Pagination"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Tag"
                              }
                            }
                          }
                        }
                      ]
                    },
                    {
                      "$ref": "#/components/schemas/EmptyResults"
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/hovercode/{qr_code_id}/tags/add/": {
      "post": {
        "operationId": "addQRCodeTags",
        "tags": [
          "Tags"
        ],
        "summary": "Add tags to a QR code",
        "description": "Send a JSON array of `{\"title\": ...}` (created if it doesn't exist) or `{\"id\": ...}` objects. Returns the code with its `tags`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/QRCodeId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/TagInput"
                },
                "minItems": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QRCodeDetail"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/hovercode/{qr_code_id}/tag/{tag}/remove/": {
      "delete": {
        "operationId": "removeQRCodeTag",
        "tags": [
          "Tags"
        ],
        "summary": "Remove a tag from a QR code",
        "description": "Removes one tag from a code (the tag itself is kept). Returns the code with its remaining `tags`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/QRCodeId"
          },
          {
            "name": "tag",
            "in": "path",
            "required": true,
            "description": "Tag id or slug.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QRCodeDetail"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/workspace/{workspace_id}/folders/": {
      "get": {
        "operationId": "listFolders",
        "tags": [
          "Folders"
        ],
        "summary": "List folders",
        "description": "The workspace's folders, by name, with full `path` breadcrumb and `code_count`. Paginated 50 per page (max 200).",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkspaceId"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize200"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Page of folders.",
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Pagination"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Folder"
                              }
                            }
                          }
                        }
                      ]
                    },
                    {
                      "$ref": "#/components/schemas/EmptyResults"
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/folder/create/": {
      "post": {
        "operationId": "createFolder",
        "tags": [
          "Folders"
        ],
        "summary": "Create a folder",
        "description": "Creates a folder, optionally nested under `parent` (id or slug). If a folder with the same name already exists under the same parent it is returned with 200 instead of creating a duplicate.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspace": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 150
                  },
                  "parent": {
                    "type": "string",
                    "description": "Folder id or slug."
                  }
                },
                "required": [
                  "workspace",
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Folder created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "200": {
            "description": "Existing folder returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/folder/{folder_id}/update/": {
      "patch": {
        "operationId": "updateFolder",
        "tags": [
          "Folders"
        ],
        "summary": "Update a folder",
        "description": "Rename (`name`; slug regenerated) and/or move (`parent`: id or slug, or null for top level). A folder can't be moved into itself or a subfolder.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FolderId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 150
                  },
                  "parent": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Folder id or slug; null = top level."
                  }
                },
                "minProperties": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/folder/{folder_id}/delete/": {
      "delete": {
        "operationId": "deleteFolder",
        "tags": [
          "Folders"
        ],
        "summary": "Delete a folder",
        "description": "Codes in it (and its subfolders) become unfiled, never deleted; subfolders move up to its parent.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FolderId"
          }
        ],
        "responses": {
          "204": {
            "$ref": "#/components/responses/NoContent"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/pdf/create/": {
      "post": {
        "operationId": "createPdfCode",
        "tags": [
          "PDF codes"
        ],
        "summary": "Create a PDF code",
        "description": "Hosts a PDF and returns a dynamic QR code pointing at it. Provide `pdf_url` or `pdf_base64` (max 20 MB). Counts toward the dynamic-code limit.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspace": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "pdf_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "pdf_base64": {
                    "type": "string",
                    "description": "Base64 PDF bytes; `data:` URI prefix allowed."
                  },
                  "filename": {
                    "type": "string",
                    "examples": [
                      "menu.pdf"
                    ]
                  },
                  "display_name": {
                    "type": "string"
                  },
                  "domain": {
                    "type": "string"
                  }
                },
                "required": [
                  "workspace"
                ],
                "anyOf": [
                  {
                    "required": [
                      "pdf_url"
                    ]
                  },
                  {
                    "required": [
                      "pdf_base64"
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PdfCode"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/link/create/": {
      "post": {
        "operationId": "createShortLink",
        "tags": [
          "Short links"
        ],
        "summary": "Create a short link",
        "description": "Creates a dynamic short link with its own QR code (SVG returned inline). Returns 200.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspace": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "link": {
                    "type": "string",
                    "description": "Destination URL."
                  },
                  "link_type": {
                    "type": "string",
                    "enum": [
                      "Link"
                    ],
                    "default": "Link"
                  },
                  "slug": {
                    "type": "string",
                    "pattern": "^[-a-zA-Z0-9]+$",
                    "description": "Unique per domain; not a reserved word. Auto-generated if omitted."
                  },
                  "domain": {
                    "type": "string"
                  },
                  "display_name": {
                    "type": "string"
                  },
                  "gps": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "workspace",
                  "link"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Link"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/workspace/{workspace_id}/links/": {
      "get": {
        "operationId": "listShortLinks",
        "tags": [
          "Short links"
        ],
        "summary": "List short links",
        "description": "Newest first. Paginated 50 per page (max 200). `q` searches destinations, display names and slugs.",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkspaceId"
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize200"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Page of short links.",
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Pagination"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/LinkListItem"
                              }
                            }
                          }
                        }
                      ]
                    },
                    {
                      "$ref": "#/components/schemas/EmptyResults"
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/link/{link_id}/": {
      "get": {
        "operationId": "getShortLink",
        "tags": [
          "Short links"
        ],
        "summary": "Get a short link",
        "description": "A single short link including its QR SVG.",
        "parameters": [
          {
            "$ref": "#/components/parameters/LinkId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Link"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/link/{link_id}/update/": {
      "patch": {
        "operationId": "updateShortLink",
        "tags": [
          "Short links"
        ],
        "summary": "Update a short link",
        "description": "Update `link`, `display_name` or `gps`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/LinkId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "link": {
                    "type": "string"
                  },
                  "display_name": {
                    "type": "string"
                  },
                  "gps": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Link"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/link/{link_id}/delete/": {
      "delete": {
        "operationId": "deleteShortLink",
        "tags": [
          "Short links"
        ],
        "summary": "Delete a short link",
        "description": "Returns 204.",
        "parameters": [
          {
            "$ref": "#/components/parameters/LinkId"
          }
        ],
        "responses": {
          "204": {
            "$ref": "#/components/responses/NoContent"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/link/{link_id}/qr/": {
      "get": {
        "operationId": "getShortLinkQR",
        "tags": [
          "Short links"
        ],
        "summary": "Short link QR image",
        "description": "`format=svg` returns the SVG inline; `format=png` (default) redirects to a generated PNG.",
        "parameters": [
          {
            "$ref": "#/components/parameters/LinkId"
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "png",
                "svg"
              ],
              "default": "png"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SVG image (format=svg).",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "302": {
            "description": "Redirect to the PNG (format=png).",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/link/{link_id}/activity/": {
      "get": {
        "operationId": "getShortLinkActivity",
        "tags": [
          "Short links"
        ],
        "summary": "Short link activity",
        "description": "Individual clicks, newest first. Same paginated format as QR code activity.",
        "parameters": [
          {
            "$ref": "#/components/parameters/LinkId"
          },
          {
            "$ref": "#/components/parameters/Start"
          },
          {
            "$ref": "#/components/parameters/End"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize200"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Pagination"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "results": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Activity"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/page/create/": {
      "post": {
        "operationId": "createPage",
        "tags": [
          "Landing pages"
        ],
        "summary": "Create a landing page",
        "description": "Creates and publishes a page in one call and returns its live URL and QR code. Counts toward the page limit.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspace": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "title": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "links": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/PageLinkInput"
                    }
                  },
                  "description": {
                    "type": "string"
                  },
                  "bg_color": {
                    "type": "string",
                    "description": "Hex color."
                  },
                  "text_color": {
                    "type": "string",
                    "description": "Hex color."
                  },
                  "domain": {
                    "type": "string"
                  },
                  "slug": {
                    "type": "string"
                  }
                },
                "required": [
                  "workspace",
                  "title"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/page/{page_id}/": {
      "get": {
        "operationId": "getPage",
        "tags": [
          "Landing pages"
        ],
        "summary": "Get a landing page",
        "description": "A single landing page.",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/workspace/{workspace_id}/pages/": {
      "get": {
        "operationId": "listPages",
        "tags": [
          "Landing pages"
        ],
        "summary": "List landing pages",
        "description": "The workspace's pages, newest first (up to 200; not paginated).",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkspaceId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Page"
                      }
                    }
                  },
                  "required": [
                    "results"
                  ]
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/form/create/": {
      "post": {
        "operationId": "createForm",
        "tags": [
          "Forms"
        ],
        "summary": "Create a form",
        "description": "Creates a draft form. Add fields with save, then publish.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspace": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "title": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "description": {
                    "type": "string"
                  }
                },
                "required": [
                  "workspace",
                  "title"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Form created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Form"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/form/{form_id}/save/": {
      "post": {
        "operationId": "saveFormFields",
        "tags": [
          "Forms"
        ],
        "summary": "Save fields",
        "description": "Saves the form's fields and optional form-level settings atomically. New fields need an `id` starting with `temp-`; existing fields keep their real `id`; omitted fields are removed. Re-publish to make changes live.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FormId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "fields": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/FormFieldInput"
                    },
                    "description": "All fields in display order; fields left out are removed."
                  },
                  "title": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "bg_color": {
                    "type": "string",
                    "description": "Hex color."
                  },
                  "text_color": {
                    "type": "string",
                    "description": "Hex color."
                  },
                  "button_color": {
                    "type": "string",
                    "description": "Hex color."
                  },
                  "button_text_color": {
                    "type": "string",
                    "description": "Hex color."
                  },
                  "thank_you_title": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "thank_you_message": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "redirect_url": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 500
                  },
                  "redirect_delay": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "outcomes": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "object"
                    }
                  },
                  "show_progress": {
                    "type": "boolean"
                  },
                  "allow_multiple_submissions": {
                    "type": "boolean"
                  },
                  "show_powered_by": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Form"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/form/{form_id}/logo-url/": {
      "post": {
        "operationId": "setFormLogoFromUrl",
        "tags": [
          "Forms"
        ],
        "summary": "Set a form's logo",
        "description": "Sets the form's logo from an image URL (JPEG/PNG/GIF/WebP, max 2 MB).",
        "parameters": [
          {
            "$ref": "#/components/parameters/FormId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "logo_url": {
                    "type": "string",
                    "format": "uri"
                  }
                },
                "required": [
                  "logo_url"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Form"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/form/{form_id}/publish/": {
      "post": {
        "operationId": "publishForm",
        "tags": [
          "Forms"
        ],
        "summary": "Publish a form",
        "description": "Snapshots the current fields as the live form, creates the short link and QR code (first publish), and sets status to `Published`. A closed form stays closed. 403 if the dynamic-code limit is reached or an image field is used without a subscription.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FormId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "domain": {
                    "type": "string"
                  },
                  "slug": {
                    "type": "string",
                    "maxLength": 150
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Form"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/form/{form_id}/close/": {
      "post": {
        "operationId": "closeForm",
        "tags": [
          "Forms"
        ],
        "summary": "Close a form",
        "description": "Stops accepting responses.",
        "parameters": [
          {
            "$ref": "#/components/parameters/FormId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Form"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/form/{form_id}/reopen/": {
      "post": {
        "operationId": "reopenForm",
        "tags": [
          "Forms"
        ],
        "summary": "Reopen a form",
        "description": "Resumes accepting responses. Only closed forms can be reopened (else 400).",
        "parameters": [
          {
            "$ref": "#/components/parameters/FormId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Form"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/forms/": {
      "get": {
        "operationId": "listForms",
        "tags": [
          "Forms"
        ],
        "summary": "List forms",
        "description": "The workspace's forms, newest first. Paginated (default 20, max 100).",
        "parameters": [
          {
            "name": "workspace",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Search by title."
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize100"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Page of forms.",
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Pagination"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/FormSummary"
                              }
                            }
                          }
                        }
                      ]
                    },
                    {
                      "$ref": "#/components/schemas/EmptyResults"
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/forms/{form_id}/responses/": {
      "get": {
        "operationId": "listFormResponses",
        "tags": [
          "Forms"
        ],
        "summary": "List responses",
        "description": "A form's submissions, newest first, with per-field answers. Paginated (default 20, max 100).",
        "parameters": [
          {
            "$ref": "#/components/parameters/FormId"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize100"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "description": "Page of responses.",
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Pagination"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "results": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/FormResponse"
                              }
                            }
                          }
                        }
                      ]
                    },
                    {
                      "$ref": "#/components/schemas/EmptyResults"
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/gs1/products/": {
      "post": {
        "operationId": "createGS1Product",
        "tags": [
          "GS1 Digital Link"
        ],
        "summary": "Create a GS1 product",
        "description": "Creates (or reuses, if the identifier already exists on the resolver domain) a GS1 product. `destination_url` seeds a default `gs1:pip` link.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GS1ProductCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Product created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GS1Product"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/workspace/{workspace_id}/gs1/products/": {
      "get": {
        "operationId": "listGS1Products",
        "tags": [
          "GS1 Digital Link"
        ],
        "summary": "List GS1 products",
        "description": "Newest first. Paginated 50 per page (max 200).",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkspaceId"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PageSize200"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Pagination"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "results": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/GS1Product"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/gs1/products/{product_id}/": {
      "get": {
        "operationId": "getGS1Product",
        "tags": [
          "GS1 Digital Link"
        ],
        "summary": "Get a GS1 product",
        "description": "A product with its link types.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProductId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GS1Product"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "patch": {
        "operationId": "updateGS1Product",
        "tags": [
          "GS1 Digital Link"
        ],
        "summary": "Update a GS1 product",
        "description": "Partial update of identifier fields.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProductId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GS1ProductUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GS1Product"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "delete": {
        "operationId": "deleteGS1Product",
        "tags": [
          "GS1 Digital Link"
        ],
        "summary": "Delete a GS1 product",
        "description": "Returns 204.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProductId"
          }
        ],
        "responses": {
          "204": {
            "$ref": "#/components/responses/NoContent"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/gs1/products/{product_id}/links/": {
      "post": {
        "operationId": "createGS1Link",
        "tags": [
          "GS1 Digital Link"
        ],
        "summary": "Add a link type",
        "description": "Adds a destination for one link type. Exactly one link is the default; setting a new default unsets the others.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProductId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GS1LinkCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Link created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GS1Link"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/gs1/products/{product_id}/links/{gs1_link_id}/": {
      "patch": {
        "operationId": "updateGS1Link",
        "tags": [
          "GS1 Digital Link"
        ],
        "summary": "Update a link type",
        "description": "Partial update.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProductId"
          },
          {
            "$ref": "#/components/parameters/GS1LinkId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GS1LinkUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GS1Link"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "delete": {
        "operationId": "deleteGS1Link",
        "tags": [
          "GS1 Digital Link"
        ],
        "summary": "Delete a link type",
        "description": "Deleting the default promotes another link. Returns 204.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProductId"
          },
          {
            "$ref": "#/components/parameters/GS1LinkId"
          }
        ],
        "responses": {
          "204": {
            "$ref": "#/components/responses/NoContent"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/gs1/products/{product_id}/linkset/": {
      "get": {
        "operationId": "getGS1Linkset",
        "tags": [
          "GS1 Digital Link"
        ],
        "summary": "Get the linkset",
        "description": "RFC 9264 linkset preview of what the resolver serves. `domain` sets the anchor domain.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProductId"
          },
          {
            "name": "domain",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Linkset.",
            "content": {
              "application/linkset+json": {
                "schema": {
                  "$ref": "#/components/schemas/Linkset"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    }
  },
  "webhooks": {
    "scan": {
      "post": {
        "operationId": "scanWebhook",
        "tags": [
          "Analytics"
        ],
        "summary": "Scan event",
        "description": "Sent to your webhook URL (Business Plus) on every scan of a dynamic QR code or short link. Verify `x-signature` against your webhook secret before processing.",
        "parameters": [
          {
            "name": "x-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Activity"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx to acknowledge."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "TokenAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Your API token with the `Token` prefix: `Authorization: Token <token>`."
      }
    },
    "parameters": {
      "QRCodeId": {
        "name": "qr_code_id",
        "in": "path",
        "required": true,
        "description": "QR code ID.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "WorkspaceId": {
        "name": "workspace_id",
        "in": "path",
        "required": true,
        "description": "Workspace ID (from account settings).",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "FolderId": {
        "name": "folder_id",
        "in": "path",
        "required": true,
        "description": "Folder ID.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "LinkId": {
        "name": "link_id",
        "in": "path",
        "required": true,
        "description": "Short link ID.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "PageId": {
        "name": "page_id",
        "in": "path",
        "required": true,
        "description": "Landing page ID.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "FormId": {
        "name": "form_id",
        "in": "path",
        "required": true,
        "description": "Form ID.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ProductId": {
        "name": "product_id",
        "in": "path",
        "required": true,
        "description": "GS1 product ID.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "GS1LinkId": {
        "name": "gs1_link_id",
        "in": "path",
        "required": true,
        "description": "GS1 link type ID.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "Page": {
        "name": "page",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1
        },
        "description": "Page number."
      },
      "PageSize200": {
        "name": "page_size",
        "in": "query",
        "description": "Results per page (default 50, max 200).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "PageSize100": {
        "name": "page_size",
        "in": "query",
        "description": "Results per page (default 20, max 100).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        }
      },
      "Start": {
        "name": "start",
        "in": "query",
        "description": "Window start (ISO date or date-time).",
        "schema": {
          "type": "string"
        },
        "examples": {
          "date": {
            "value": "2026-01-01"
          }
        }
      },
      "End": {
        "name": "end",
        "in": "query",
        "description": "Window end, inclusive (ISO date or date-time).",
        "schema": {
          "type": "string"
        },
        "examples": {
          "date": {
            "value": "2026-03-31"
          }
        }
      },
      "Days": {
        "name": "days",
        "in": "query",
        "description": "Rolling look-back in days (1-365).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 365
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Validation error.",
        "content": {
          "application/json": {
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/Error"
                },
                {
                  "$ref": "#/components/schemas/FieldErrors"
                }
              ]
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API token.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Detail"
            },
            "examples": {
              "missing": {
                "value": {
                  "detail": "Authentication credentials were not provided."
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Not permitted: plan limit or paid feature, or (on form endpoints) a missing or invalid token.",
        "content": {
          "application/json": {
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/Detail"
                },
                {
                  "$ref": "#/components/schemas/Error"
                }
              ]
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found, or not in a workspace you belong to.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Detail"
            },
            "examples": {
              "notFound": {
                "value": {
                  "detail": "Error 404, page not found"
                }
              }
            }
          }
        }
      },
      "UnsupportedMediaType": {
        "description": "Request body was not JSON.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "e": {
                "value": {
                  "error": "Invalid content type. JSON data is required."
                }
              }
            }
          }
        }
      },
      "NoContent": {
        "description": "Deleted."
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded. Wait `Retry-After` seconds before retrying.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Error envelope. Most validation failures are returned as `{\"error\": ...}` where `error` is a message string, a field-error object (`{\"field\": [\"message\"]}`) or a list of messages. Authentication, permission and not-found errors use DRF's `{\"detail\": \"...\"}` shape instead. The structured `{code, message}` form is reserved and may appear in future responses.",
        "properties": {
          "error": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  }
                },
                "required": [
                  "code",
                  "message"
                ]
              },
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "object",
                "additionalProperties": true
              }
            ]
          },
          "detail": {
            "type": "string"
          }
        }
      },
      "FieldErrors": {
        "description": "DRF validation errors returned without the `error` wrapper: an object mapping field names to lists of messages, or a bare list of messages.",
        "oneOf": [
          {
            "type": "object",
            "additionalProperties": true,
            "examples": [
              {
                "name": [
                  "Please add a folder name."
                ]
              }
            ]
          },
          {
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "Please add your Workspace ID"
              ]
            ]
          }
        ]
      },
      "Detail": {
        "type": "object",
        "properties": {
          "detail": {
            "type": "string"
          }
        },
        "required": [
          "detail"
        ],
        "examples": [
          {
            "detail": "Error 404, page not found"
          }
        ]
      },
      "Pagination": {
        "type": "object",
        "description": "DRF page-number pagination envelope.",
        "properties": {
          "count": {
            "type": "integer"
          },
          "next": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "previous": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "results": {
            "type": "array"
          }
        },
        "required": [
          "count",
          "next",
          "previous",
          "results"
        ]
      },
      "EmptyResults": {
        "type": "object",
        "description": "Returned instead of a pagination envelope when the list is empty.",
        "properties": {
          "message": {
            "type": "string",
            "const": "No results found"
          },
          "results": {
            "type": "array",
            "maxItems": 0
          }
        },
        "required": [
          "message",
          "results"
        ]
      },
      "FolderRef": {
        "type": [
          "object",
          "null"
        ],
        "description": "The folder a code is filed in, or null when unfiled.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "path": {
            "type": "string",
            "description": "Full breadcrumb, e.g. `Campaigns / Spring 2026`."
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "path"
        ]
      },
      "Folder": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "path": {
            "type": "string"
          },
          "parent": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "code_count": {
            "type": "integer"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "path",
          "parent",
          "code_count",
          "created"
        ]
      },
      "Tag": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "title",
          "slug"
        ]
      },
      "TagInput": {
        "type": "object",
        "description": "Either `title` (tag is created if it doesn't exist) or `id` of an existing tag.",
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 150
          },
          "id": {
            "type": "string",
            "format": "uuid"
          }
        },
        "anyOf": [
          {
            "required": [
              "title"
            ]
          },
          {
            "required": [
              "id"
            ]
          }
        ]
      },
      "Activity": {
        "type": "object",
        "description": "One tracked scan/click.",
        "properties": {
          "qr_code_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "time_utc": {
            "type": "string",
            "examples": [
              "2026-06-01 17:44:48.920050+00:00"
            ]
          },
          "time_timezone_aware": {
            "type": "string",
            "examples": [
              "Jun. 1, 2026, 05:44 p.m."
            ]
          },
          "location": {
            "type": [
              "string",
              "null"
            ]
          },
          "device": {
            "type": [
              "string",
              "null"
            ]
          },
          "scanner_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hashed, anonymous scanner fingerprint."
          },
          "id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "VCard": {
        "type": "object",
        "properties": {
          "first_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "mobile_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "website": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "company_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "position": {
            "type": [
              "string",
              "null"
            ]
          },
          "street": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "post_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "VCardInput": {
        "type": "object",
        "properties": {
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "mobile_number": {
            "type": "string"
          },
          "phone_number": {
            "type": "string"
          },
          "website": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "company_name": {
            "type": "string"
          },
          "position": {
            "type": "string"
          },
          "street": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "post_code": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        },
        "required": [
          "first_name"
        ]
      },
      "GS1ProductSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "identifier_type": {
            "type": "string",
            "enum": [
              "01",
              "00",
              "414",
              "417",
              "253",
              "255",
              "401",
              "402",
              "8003",
              "8004",
              "8006",
              "8010",
              "8013",
              "8017",
              "8018"
            ]
          },
          "identifier_value": {
            "type": "string"
          },
          "digital_link_path": {
            "type": "string"
          }
        }
      },
      "QRCode": {
        "type": "object",
        "description": "A QR code (Hovercode). `vcard` / `gs1_product` are present only for those types.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "qr_data": {
            "type": [
              "string",
              "null"
            ],
            "description": "Link URL, plain text, or (GS1) the Digital Link short URL. Omitted for vCard codes."
          },
          "qr_type": {
            "type": "string",
            "enum": [
              "Link",
              "PDF",
              "Text",
              "vCard",
              "App stores",
              "Call",
              "Send email",
              "Send message",
              "Page",
              "Wifi",
              "Form",
              "GS1"
            ]
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "shortlink_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Short link the code encodes; null for static codes."
          },
          "dynamic": {
            "type": "boolean"
          },
          "frame": {
            "type": [
              "string",
              "null"
            ],
            "description": "Frame slug."
          },
          "frame_color": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "#111111"
            ]
          },
          "logo": {
            "type": [
              "string",
              "null"
            ],
            "description": "Logo image URL."
          },
          "primary_color": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "#111111"
            ]
          },
          "eye_style": {
            "type": "string",
            "enum": [
              "Square",
              "Rounded",
              "Drop",
              "Leaf"
            ]
          },
          "eye_color": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "#111111"
            ]
          },
          "background_color": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hex color; null when transparent."
          },
          "pattern": {
            "type": "string",
            "enum": [
              "Original",
              "Circles",
              "Squares",
              "Diamonds",
              "Triangles",
              "Lines"
            ]
          },
          "text": {
            "type": [
              "string",
              "null"
            ]
          },
          "text_secondary": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_border": {
            "type": "boolean"
          },
          "svg": {
            "type": [
              "string",
              "null"
            ],
            "description": "Inline SVG markup."
          },
          "svg_file": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "png": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "gps_tracking": {
            "type": "boolean"
          },
          "folder": {
            "$ref": "#/components/schemas/FolderRef"
          },
          "vcard": {
            "$ref": "#/components/schemas/VCard"
          },
          "gs1_product": {
            "$ref": "#/components/schemas/GS1ProductSummary"
          },
          "error_correction": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "L",
              "M",
              "Q",
              "H",
              null
            ]
          }
        },
        "required": [
          "id",
          "qr_type",
          "dynamic",
          "created"
        ]
      },
      "QRCodeDetail": {
        "type": "object",
        "description": "A QR code with lifetime scan totals and tags (single-code and tag endpoints).",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "qr_data": {
            "type": [
              "string",
              "null"
            ],
            "description": "Link URL, plain text, or (GS1) the Digital Link short URL. Omitted for vCard codes."
          },
          "qr_type": {
            "type": "string",
            "enum": [
              "Link",
              "PDF",
              "Text",
              "vCard",
              "App stores",
              "Call",
              "Send email",
              "Send message",
              "Page",
              "Wifi",
              "Form",
              "GS1"
            ]
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "shortlink_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Short link the code encodes; null for static codes."
          },
          "dynamic": {
            "type": "boolean"
          },
          "frame": {
            "type": [
              "string",
              "null"
            ],
            "description": "Frame slug."
          },
          "frame_color": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "#111111"
            ]
          },
          "logo": {
            "type": [
              "string",
              "null"
            ],
            "description": "Logo image URL."
          },
          "primary_color": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "#111111"
            ]
          },
          "eye_style": {
            "type": "string",
            "enum": [
              "Square",
              "Rounded",
              "Drop",
              "Leaf"
            ]
          },
          "eye_color": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "#111111"
            ]
          },
          "background_color": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hex color; null when transparent."
          },
          "pattern": {
            "type": "string",
            "enum": [
              "Original",
              "Circles",
              "Squares",
              "Diamonds",
              "Triangles",
              "Lines"
            ]
          },
          "text": {
            "type": [
              "string",
              "null"
            ]
          },
          "text_secondary": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_border": {
            "type": "boolean"
          },
          "svg": {
            "type": [
              "string",
              "null"
            ],
            "description": "Inline SVG markup."
          },
          "svg_file": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "png": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "gps_tracking": {
            "type": "boolean"
          },
          "folder": {
            "$ref": "#/components/schemas/FolderRef"
          },
          "vcard": {
            "$ref": "#/components/schemas/VCard"
          },
          "gs1_product": {
            "$ref": "#/components/schemas/GS1ProductSummary"
          },
          "total_scans": {
            "type": "integer"
          },
          "unique_scans": {
            "type": "integer"
          },
          "tags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Tag"
            }
          }
        },
        "required": [
          "id",
          "qr_type",
          "dynamic",
          "created"
        ]
      },
      "QRCodeListItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "qr_data": {
            "type": [
              "string",
              "null"
            ]
          },
          "qr_type": {
            "type": "string",
            "enum": [
              "Link",
              "PDF",
              "Text",
              "vCard",
              "App stores",
              "Call",
              "Send email",
              "Send message",
              "Page",
              "Wifi",
              "Form",
              "GS1"
            ]
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "shortlink_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "png": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "dynamic": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "folder": {
            "$ref": "#/components/schemas/FolderRef"
          },
          "total_scans": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Only with `include=scans`; null for codes that can't be tracked."
          },
          "unique_scans": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Only with `include=scans`."
          },
          "tags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Tag"
            },
            "description": "Only with `include=tags`."
          }
        },
        "required": [
          "id",
          "qr_type",
          "dynamic",
          "created",
          "folder"
        ]
      },
      "PdfCode": {
        "allOf": [
          {
            "$ref": "#/components/schemas/QRCode"
          },
          {
            "type": "object",
            "properties": {
              "pdf_file_url": {
                "type": "string",
                "format": "uri",
                "description": "The raw hosted PDF file."
              }
            }
          }
        ]
      },
      "QRCodeCreate": {
        "type": "object",
        "properties": {
          "workspace": {
            "type": "string",
            "format": "uuid",
            "description": "Your workspace ID."
          },
          "qr_data": {
            "type": "string",
            "description": "For `Link`: a valid URL. For `Text`: any plain text."
          },
          "qr_type": {
            "type": "string",
            "enum": [
              "Link",
              "Text",
              "vCard",
              "GS1"
            ],
            "default": "Link"
          },
          "vcard": {
            "$ref": "#/components/schemas/VCardInput"
          },
          "gs1_product": {
            "description": "For `qr_type=GS1`: an object creating the product inline, or the id of an existing GS1 product.",
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "identifier_type": {
                    "type": "string",
                    "enum": [
                      "01",
                      "00",
                      "414",
                      "417",
                      "253",
                      "255",
                      "401",
                      "402",
                      "8003",
                      "8004",
                      "8006",
                      "8010",
                      "8013",
                      "8017",
                      "8018"
                    ],
                    "default": "01"
                  },
                  "identifier_value": {
                    "type": "string"
                  },
                  "batch_lot": {
                    "type": "string",
                    "maxLength": 20
                  },
                  "serial_number": {
                    "type": "string",
                    "maxLength": 20
                  },
                  "expiry_date": {
                    "type": "string",
                    "pattern": "^[0-9]{6}$"
                  },
                  "destination_url": {
                    "type": "string"
                  }
                },
                "required": [
                  "identifier_value",
                  "destination_url"
                ]
              },
              {
                "type": "string",
                "format": "uuid"
              }
            ]
          },
          "dynamic": {
            "type": "boolean",
            "default": false
          },
          "display_name": {
            "type": "string",
            "maxLength": 200
          },
          "domain": {
            "type": "string",
            "description": "Custom short-link domain for dynamic codes."
          },
          "generate_png": {
            "type": "boolean",
            "default": false,
            "description": "Also generate `.png`/`.svg` files synchronously (slower)."
          },
          "gps_tracking": {
            "type": "boolean"
          },
          "error_correction": {
            "type": "string",
            "enum": [
              "L",
              "M",
              "Q",
              "H"
            ]
          },
          "size": {
            "type": "integer",
            "default": 220,
            "description": "Width in pixels."
          },
          "logo_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 500
          },
          "logo_round": {
            "type": "boolean"
          },
          "folder": {
            "type": "string",
            "description": "Folder id or slug."
          },
          "template": {
            "type": "string",
            "format": "uuid",
            "description": "ID of a design template saved in your workspace; explicit fields override it."
          },
          "primary_color": {
            "type": "string",
            "description": "Hex color with `#`.",
            "examples": [
              "#1DA1F2"
            ]
          },
          "eye_color": {
            "type": "string",
            "maxLength": 7
          },
          "background_color": {
            "type": "string",
            "maxLength": 7,
            "description": "Hex color; transparent by default."
          },
          "pattern": {
            "type": "string",
            "enum": [
              "Original",
              "Circles",
              "Squares",
              "Diamonds",
              "Triangles",
              "Lines"
            ],
            "default": "Original"
          },
          "eye_style": {
            "type": "string",
            "enum": [
              "Square",
              "Rounded",
              "Drop",
              "Leaf"
            ],
            "default": "Square"
          },
          "frame": {
            "type": "string",
            "description": "Frame slug. Documented values: `border`, `border-small`, `border-large`, `square`, `speech-bubble`, `speech-bubble-above`, `card`, `card-above`, `text-frame`, `round-frame`, `circle-viewfinder`, `solid-spin`, `burst`, `scattered-lines`, `polkadot`, `swirl`. Frames are stored server-side; an unknown slug returns 400."
          },
          "frame_color": {
            "type": "string"
          },
          "has_border": {
            "type": "boolean"
          },
          "text": {
            "type": "string",
            "maxLength": 100
          },
          "text_secondary": {
            "type": "string",
            "maxLength": 100
          }
        },
        "required": [
          "workspace"
        ],
        "allOf": [
          {
            "if": {
              "properties": {
                "qr_type": {
                  "const": "vCard"
                }
              },
              "required": [
                "qr_type"
              ]
            },
            "then": {
              "required": [
                "vcard"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "qr_type": {
                  "const": "GS1"
                }
              },
              "required": [
                "qr_type"
              ]
            },
            "then": {
              "required": [
                "gs1_product"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "qr_type": {
                  "enum": [
                    "Link",
                    "Text"
                  ]
                }
              }
            },
            "then": {
              "required": [
                "qr_data"
              ]
            }
          }
        ]
      },
      "QRCodeUpdate": {
        "type": "object",
        "properties": {
          "display_name": {
            "type": "string",
            "maxLength": 200
          },
          "qr_data": {
            "type": "string",
            "description": "New destination URL (dynamic codes only)."
          },
          "gps_tracking": {
            "type": "boolean"
          },
          "folder": {
            "type": [
              "string",
              "null"
            ],
            "description": "Folder id or slug; null removes it from its folder."
          }
        }
      },
      "QRCodeDesign": {
        "type": "object",
        "properties": {
          "primary_color": {
            "type": "string",
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "examples": [
              "#1DA1F2"
            ]
          },
          "eye_color": {
            "type": "string",
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "examples": [
              "#1DA1F2"
            ]
          },
          "frame_color": {
            "type": "string",
            "pattern": "^#[0-9A-Fa-f]{6}$",
            "examples": [
              "#1DA1F2"
            ]
          },
          "background_color": {
            "type": "string",
            "description": "Hex color, or `transparent`."
          },
          "pattern": {
            "type": "string",
            "enum": [
              "Original",
              "Circles",
              "Squares",
              "Diamonds",
              "Triangles",
              "Lines"
            ]
          },
          "eye_style": {
            "type": "string",
            "enum": [
              "Square",
              "Rounded",
              "Drop",
              "Leaf"
            ]
          },
          "frame": {
            "type": "string",
            "description": "Frame slug. Documented values: `border`, `border-small`, `border-large`, `square`, `speech-bubble`, `speech-bubble-above`, `card`, `card-above`, `text-frame`, `round-frame`, `circle-viewfinder`, `solid-spin`, `burst`, `scattered-lines`, `polkadot`, `swirl`. Frames are stored server-side; an unknown slug returns 400. Empty string removes the frame."
          },
          "has_border": {
            "type": "boolean"
          },
          "text": {
            "type": "string",
            "maxLength": 100
          },
          "text_secondary": {
            "type": "string",
            "maxLength": 100
          },
          "error_correction": {
            "type": "string",
            "enum": [
              "L",
              "M",
              "Q",
              "H"
            ]
          },
          "logo_url": {
            "type": "string",
            "format": "uri"
          },
          "logo_base64": {
            "type": "string",
            "description": "JPEG/PNG/GIF/WebP under 2 MB, base64; `data:` prefix allowed."
          },
          "logo_round": {
            "type": "boolean"
          },
          "remove_logo": {
            "type": "boolean"
          }
        }
      },
      "Template": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "primary_color": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "#111111"
            ]
          },
          "background_color": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "#111111"
            ]
          },
          "pattern": {
            "type": "string",
            "enum": [
              "Original",
              "Circles",
              "Squares",
              "Diamonds",
              "Triangles",
              "Lines"
            ]
          },
          "eye_style": {
            "type": "string",
            "enum": [
              "Square",
              "Rounded",
              "Drop",
              "Leaf"
            ]
          },
          "eye_color": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "#111111"
            ]
          },
          "frame_color": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "#111111"
            ]
          },
          "has_border": {
            "type": "boolean"
          },
          "text": {
            "type": [
              "string",
              "null"
            ]
          },
          "text_secondary": {
            "type": [
              "string",
              "null"
            ]
          },
          "frame": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_logo": {
            "type": "boolean"
          },
          "logo": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "preview": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "is_default": {
            "type": "boolean",
            "description": "True for a Hovercode built-in design, false for a workspace template."
          }
        }
      },
      "ScanAnalytics": {
        "type": "object",
        "properties": {
          "qr_code_id": {
            "type": "string",
            "format": "uuid"
          },
          "period_days": {
            "type": "integer"
          },
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time"
          },
          "total_scans": {
            "type": "integer"
          },
          "unique_scans": {
            "type": "integer"
          },
          "scans_by_day": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date"
                },
                "scans": {
                  "type": "integer"
                }
              }
            }
          },
          "top_countries": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string"
                },
                "scans": {
                  "type": "integer"
                }
              }
            }
          },
          "top_cities": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string"
                },
                "scans": {
                  "type": "integer"
                }
              }
            }
          },
          "top_os": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string"
                },
                "scans": {
                  "type": "integer"
                }
              }
            }
          },
          "top_browsers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string"
                },
                "scans": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "WorkspaceAnalytics": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ScanAnalytics"
          },
          {
            "type": "object",
            "properties": {
              "workspace": {
                "type": "string",
                "format": "uuid"
              },
              "top_codes": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "display_name": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "destination": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "scans": {
                      "type": "integer"
                    },
                    "unique_scans": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "Download": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "png_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "svg_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          }
        },
        "required": [
          "id",
          "png_url",
          "svg_url"
        ]
      },
      "LinkListItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "link": {
            "type": "string",
            "description": "Destination URL."
          },
          "link_type": {
            "type": "string",
            "enum": [
              "Link"
            ]
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "short_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "total_clicks": {
            "type": "integer"
          },
          "unique_clicks": {
            "type": "integer"
          },
          "png": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "link",
          "short_url",
          "created"
        ]
      },
      "Link": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "link": {
            "type": "string",
            "description": "Destination URL."
          },
          "link_type": {
            "type": "string",
            "enum": [
              "Link"
            ]
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "short_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "total_clicks": {
            "type": "integer"
          },
          "unique_clicks": {
            "type": "integer"
          },
          "png": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "gps": {
            "type": "boolean"
          },
          "svg": {
            "type": "string",
            "description": "The short link's QR code as inline SVG."
          }
        },
        "required": [
          "id",
          "link",
          "short_url",
          "created"
        ]
      },
      "PageLink": {
        "type": "object",
        "properties": {
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "default": "Website",
            "description": "Button type, e.g. `Website`, `Instagram`, `YouTube`, `LinkedIn`, `TikTok`."
          }
        }
      },
      "PageLinkInput": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "default": "Website",
            "description": "Button type, e.g. `Website`, `Instagram`, `YouTube`, `LinkedIn`, `TikTok`."
          }
        },
        "required": [
          "url"
        ]
      },
      "Page": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "Pending",
              "Draft",
              "Published",
              "Deleted"
            ]
          },
          "layout_version": {
            "type": [
              "integer",
              "null"
            ]
          },
          "share_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "qr_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The page's QR code id (use with the QR code endpoints)."
          },
          "svg": {
            "type": [
              "string",
              "null"
            ],
            "description": "QR code SVG; null until the page's URL is final."
          },
          "links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PageLink"
            }
          },
          "created": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "title",
          "status",
          "share_url",
          "links",
          "created"
        ]
      },
      "FormFieldOption": {
        "type": "object",
        "properties": {
          "value": {},
          "label": {
            "type": "string"
          }
        }
      },
      "FormField": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "field_type": {
            "type": "string",
            "enum": [
              "text",
              "email",
              "phone",
              "number",
              "textarea",
              "select",
              "radio",
              "checkbox",
              "date",
              "time",
              "url",
              "rating",
              "scale",
              "yes_no",
              "image",
              "image_choice",
              "ranking",
              "statement",
              "hidden",
              "page_break"
            ]
          },
          "label": {
            "type": "string",
            "maxLength": 500
          },
          "placeholder": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "help_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "required": {
            "type": "boolean",
            "default": false
          },
          "min_length": {
            "type": [
              "integer",
              "null"
            ]
          },
          "max_length": {
            "type": [
              "integer",
              "null"
            ]
          },
          "min_value": {
            "type": [
              "number",
              "null"
            ]
          },
          "max_value": {
            "type": [
              "number",
              "null"
            ]
          },
          "pattern": {
            "type": [
              "string",
              "null"
            ]
          },
          "options": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/FormFieldOption"
            },
            "description": "For `select`/`radio`/`checkbox`."
          },
          "logic": {
            "type": [
              "object",
              "null"
            ],
            "description": "Conditional logic rules."
          },
          "settings": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "min_label": {
                "type": "string"
              },
              "max_label": {
                "type": "string"
              },
              "allow_other": {
                "type": "boolean"
              },
              "randomize": {
                "type": "boolean"
              }
            }
          },
          "order": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "field_type",
          "label",
          "order"
        ]
      },
      "FormFieldInput": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Existing field's id to keep it, or a new id starting with `temp-` to create one."
          },
          "field_type": {
            "type": "string",
            "enum": [
              "text",
              "email",
              "phone",
              "number",
              "textarea",
              "select",
              "radio",
              "checkbox",
              "date",
              "time",
              "url",
              "rating",
              "scale",
              "yes_no",
              "image",
              "image_choice",
              "ranking",
              "statement",
              "hidden",
              "page_break"
            ]
          },
          "label": {
            "type": "string",
            "maxLength": 500
          },
          "placeholder": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "help_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "required": {
            "type": "boolean",
            "default": false
          },
          "min_length": {
            "type": [
              "integer",
              "null"
            ]
          },
          "max_length": {
            "type": [
              "integer",
              "null"
            ]
          },
          "min_value": {
            "type": [
              "number",
              "null"
            ]
          },
          "max_value": {
            "type": [
              "number",
              "null"
            ]
          },
          "pattern": {
            "type": [
              "string",
              "null"
            ]
          },
          "options": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/FormFieldOption"
            },
            "description": "For `select`/`radio`/`checkbox`."
          },
          "logic": {
            "type": [
              "object",
              "null"
            ],
            "description": "Conditional logic rules."
          },
          "settings": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "min_label": {
                "type": "string"
              },
              "max_label": {
                "type": "string"
              },
              "allow_other": {
                "type": "boolean"
              },
              "randomize": {
                "type": "boolean"
              }
            }
          }
        },
        "required": [
          "field_type",
          "label"
        ]
      },
      "Form": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "workspace_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "Draft",
              "Published",
              "Closed",
              "Deleted"
            ]
          },
          "logo": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "bg_color": {
            "type": "string",
            "description": "Hex color."
          },
          "text_color": {
            "type": "string",
            "description": "Hex color."
          },
          "button_color": {
            "type": "string",
            "description": "Hex color."
          },
          "button_text_color": {
            "type": "string",
            "description": "Hex color."
          },
          "thank_you_title": {
            "type": "string",
            "maxLength": 200
          },
          "thank_you_message": {
            "type": [
              "string",
              "null"
            ]
          },
          "redirect_url": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "redirect_delay": {
            "type": "integer",
            "minimum": 0
          },
          "outcomes": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "object"
            }
          },
          "show_progress": {
            "type": "boolean"
          },
          "allow_multiple_submissions": {
            "type": "boolean"
          },
          "show_powered_by": {
            "type": "boolean"
          },
          "notification_recipients": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "can_use_notifications": {
            "type": "boolean"
          },
          "can_use_file_uploads": {
            "type": "boolean"
          },
          "current_user_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "current_user_email_verified": {
            "type": "boolean"
          },
          "current_user_email": {
            "type": [
              "string",
              "null"
            ]
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FormField"
            }
          },
          "shortlink_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "shortlink_domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "shortlink_slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "response_count": {
            "type": "integer"
          },
          "has_unpublished_changes": {
            "type": "boolean"
          },
          "qr_code_svg": {
            "type": [
              "string",
              "null"
            ],
            "description": "Null until published."
          },
          "qr_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "share_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Public respondent link; null unless the form is Published."
          },
          "dashboard_url": {
            "type": "string",
            "format": "uri"
          },
          "can_remove_powered_by": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "modified": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "title",
          "status",
          "fields"
        ]
      },
      "FormSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "Draft",
              "Published",
              "Closed",
              "Deleted"
            ]
          },
          "response_count": {
            "type": "integer"
          },
          "share_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "workspace": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "modified": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FormResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "submitted_at": {
            "type": "string",
            "format": "date-time"
          },
          "is_complete": {
            "type": "boolean"
          },
          "completion_time_seconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "outcome": {},
          "field_responses": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "field_label": {
                  "type": "string"
                },
                "field_type": {
                  "type": "string",
                  "enum": [
                    "text",
                    "email",
                    "phone",
                    "number",
                    "textarea",
                    "select",
                    "radio",
                    "checkbox",
                    "date",
                    "time",
                    "url",
                    "rating",
                    "scale",
                    "yes_no",
                    "image",
                    "image_choice",
                    "ranking",
                    "statement",
                    "hidden",
                    "page_break"
                  ]
                },
                "value": {
                  "description": "The answer, formatted for display."
                }
              }
            }
          }
        }
      },
      "GS1Link": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          },
          "link_type": {
            "type": "string",
            "enum": [
              "gs1:pip",
              "gs1:quickStartGuide",
              "gs1:instructions",
              "gs1:safetyInfo",
              "gs1:recipeInfo",
              "gs1:traceability",
              "gs1:hasRetailers",
              "gs1:recallStatus",
              "gs1:review",
              "gs1:ePIL",
              "gs1:productSustainabilityInfo",
              "gs1:promotion",
              "gs1:masterData",
              "gs1:smpc",
              "gs1:certificationInfo",
              "gs1:registerProduct"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "destination_url": {
            "type": "string",
            "maxLength": 800
          },
          "is_default": {
            "type": "boolean",
            "default": false
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 10,
            "examples": [
              "en"
            ]
          },
          "media_type": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "default": "text/html"
          }
        },
        "required": [
          "id",
          "link_type",
          "title",
          "destination_url",
          "is_default"
        ]
      },
      "GS1LinkCreate": {
        "type": "object",
        "properties": {
          "link_type": {
            "type": "string",
            "enum": [
              "gs1:pip",
              "gs1:quickStartGuide",
              "gs1:instructions",
              "gs1:safetyInfo",
              "gs1:recipeInfo",
              "gs1:traceability",
              "gs1:hasRetailers",
              "gs1:recallStatus",
              "gs1:review",
              "gs1:ePIL",
              "gs1:productSustainabilityInfo",
              "gs1:promotion",
              "gs1:masterData",
              "gs1:smpc",
              "gs1:certificationInfo",
              "gs1:registerProduct"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "destination_url": {
            "type": "string",
            "maxLength": 800
          },
          "is_default": {
            "type": "boolean",
            "default": false
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 10,
            "examples": [
              "en"
            ]
          },
          "media_type": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "default": "text/html"
          }
        },
        "required": [
          "link_type",
          "title",
          "destination_url"
        ]
      },
      "GS1LinkUpdate": {
        "type": "object",
        "properties": {
          "link_type": {
            "type": "string",
            "enum": [
              "gs1:pip",
              "gs1:quickStartGuide",
              "gs1:instructions",
              "gs1:safetyInfo",
              "gs1:recipeInfo",
              "gs1:traceability",
              "gs1:hasRetailers",
              "gs1:recallStatus",
              "gs1:review",
              "gs1:ePIL",
              "gs1:productSustainabilityInfo",
              "gs1:promotion",
              "gs1:masterData",
              "gs1:smpc",
              "gs1:certificationInfo",
              "gs1:registerProduct"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "destination_url": {
            "type": "string",
            "maxLength": 800
          },
          "is_default": {
            "type": "boolean",
            "default": false
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 10,
            "examples": [
              "en"
            ]
          },
          "media_type": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "default": "text/html"
          }
        }
      },
      "GS1Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "identifier_type": {
            "type": "string",
            "enum": [
              "01",
              "00",
              "414",
              "417",
              "253",
              "255",
              "401",
              "402",
              "8003",
              "8004",
              "8006",
              "8010",
              "8013",
              "8017",
              "8018"
            ],
            "default": "01",
            "description": "GS1 Application Identifier (01 = GTIN)."
          },
          "identifier_value": {
            "type": "string",
            "maxLength": 30,
            "description": "Validated, including the GS1 check digit where applicable."
          },
          "batch_lot": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          },
          "serial_number": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          },
          "expiry_date": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9]{6}$",
            "description": "YYMMDD."
          },
          "domain": {
            "type": [
              "string",
              "null"
            ],
            "description": "Resolver domain; set at create time."
          },
          "identifier_label": {
            "type": "string"
          },
          "digital_link_path": {
            "type": "string"
          },
          "link_types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GS1Link"
            }
          },
          "created": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "identifier_type",
          "identifier_value",
          "link_types"
        ]
      },
      "GS1ProductCreate": {
        "type": "object",
        "properties": {
          "workspace": {
            "type": "string",
            "format": "uuid"
          },
          "identifier_type": {
            "type": "string",
            "enum": [
              "01",
              "00",
              "414",
              "417",
              "253",
              "255",
              "401",
              "402",
              "8003",
              "8004",
              "8006",
              "8010",
              "8013",
              "8017",
              "8018"
            ],
            "default": "01",
            "description": "GS1 Application Identifier (01 = GTIN)."
          },
          "identifier_value": {
            "type": "string",
            "maxLength": 30,
            "description": "Validated, including the GS1 check digit where applicable."
          },
          "batch_lot": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          },
          "serial_number": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          },
          "expiry_date": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9]{6}$",
            "description": "YYMMDD."
          },
          "destination_url": {
            "type": "string",
            "description": "Seeds a default `gs1:pip` link."
          },
          "domain": {
            "type": "string",
            "description": "Resolver domain available to your workspace (optional)."
          }
        },
        "required": [
          "workspace",
          "identifier_value"
        ]
      },
      "GS1ProductUpdate": {
        "type": "object",
        "properties": {
          "identifier_type": {
            "type": "string",
            "enum": [
              "01",
              "00",
              "414",
              "417",
              "253",
              "255",
              "401",
              "402",
              "8003",
              "8004",
              "8006",
              "8010",
              "8013",
              "8017",
              "8018"
            ],
            "default": "01",
            "description": "GS1 Application Identifier (01 = GTIN)."
          },
          "identifier_value": {
            "type": "string",
            "maxLength": 30,
            "description": "Validated, including the GS1 check digit where applicable."
          },
          "batch_lot": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          },
          "serial_number": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          },
          "expiry_date": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9]{6}$",
            "description": "YYMMDD."
          }
        }
      },
      "Linkset": {
        "type": "object",
        "description": "RFC 9264 linkset.",
        "properties": {
          "linkset": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "anchor": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "additionalProperties": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "href": {
                      "type": "string",
                      "format": "uri"
                    },
                    "title": {
                      "type": "string"
                    },
                    "hreflang": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "type": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "RateLimitError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "retry_after"
            ],
            "properties": {
              "code": {
                "type": "string",
                "const": "rate_limited"
              },
              "message": {
                "type": "string"
              },
              "retry_after": {
                "type": "integer",
                "description": "Seconds to wait before retrying."
              },
              "docs": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    },
    "headers": {
      "RateLimit-Limit": {
        "description": "Requests allowed in the current one-minute window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the window resets.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Policy": {
        "description": "The limit and window, e.g. `60;w=60`.",
        "schema": {
          "type": "string"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      }
    }
  }
}
